diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..0a44435 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a9b9ff2 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + EditorConfig + + + Java + + + JavaScript 和 TypeScript + + + Markdown + + + XPath + + + 代码样式问题JavaScript 和 TypeScript + + + 可移植性Java + + + 国际化 + + + 国际化Java + + + 属性文件Java + + + + + 用户定义 + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cool-admin-java/.gitignore b/cool-admin-java/.gitignore new file mode 100644 index 0000000..d5ae312 --- /dev/null +++ b/cool-admin-java/.gitignore @@ -0,0 +1,37 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +assets/ +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.DS_Store +lib +plugin \ No newline at end of file diff --git a/cool-admin-java/Dockerfile b/cool-admin-java/Dockerfile new file mode 100644 index 0000000..1108bb7 --- /dev/null +++ b/cool-admin-java/Dockerfile @@ -0,0 +1,14 @@ +# 使用 GraalVM 17 作为基础镜像 +FROM ghcr.io/graalvm/graalvm-ce:latest + +# 设置容器内的工作目录 +WORKDIR /app + +# 将可执行的jar文件复制到容器内 +COPY target/cool-admin-8.0.0.jar /app/cool-admin-8.0.0.jar + +# 暴露Spring Boot应用程序运行的端口 +EXPOSE 8001 + +# 运行Spring Boot应用程序的命令 +ENTRYPOINT ["java", "-jar", "/app/cool-admin-8.0.0.jar", "--spring.profiles.active=prod"] diff --git a/cool-admin-java/LICENSE b/cool-admin-java/LICENSE new file mode 100644 index 0000000..5a6831d --- /dev/null +++ b/cool-admin-java/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 cool-team-official + +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/cool-admin-java/README.md b/cool-admin-java/README.md new file mode 100644 index 0000000..02f0c7e --- /dev/null +++ b/cool-admin-java/README.md @@ -0,0 +1,103 @@ + +

+ Midway Logo +

+

cool-admin(java版)后台权限管理系统,开源免费,Ai编码、流程编排、模块化、插件化,用于快速构建后台应用程序,详情可到官网 进一步了解。 +

+ GitHub license + GitHub tag + GitHub tag +

+ +## 技术栈 + +- 后端:**`Springboot3` `Mybatis-Flex`** +- 前端:**`Vue3` `Vite` `Element-Ui` `Typescript`** +- 数据库:**`Mysql` `Postgresql` `Sqlite(适配中)` `...`** + +## 特性 + +Ai时代,很多老旧的框架已经无法满足现代化的开发需求,Cool-Admin开发了一系列的功能,让开发变得更简单、更快速、更高效。 + +- **Ai编码**:通过微调大模型学习框架特有写法,实现简单功能从Api接口到前端页面的一键生成[详情](https://java.cool-admin.com/src/guide/ai.html) +- **流程编排**:通过拖拽编排方式,即可实现类似像智能客服这样的功能[详情](https://cool-js.com/plugin/118) +- **多租户**:支持多租户,采用全局动态注入查询条件[详情](https://java.cool-admin.com/src/guide/tenant.html) +- **多语言**:基于大模型自动翻译,无需更改原有代码[详情](https://java.cool-admin.com/src/guide/i18n.html) +- **模块化**:代码是模块化的,清晰明了,方便维护 +- **插件化**:插件化的设计,可以通过安装插件的方式扩展如:支付、短信、邮件等功能 +- **自动初始化**:数据自动化,无需再手动维护,启动时自动生成数据库表和表结构数据 +- **cool-admin-java-plus**: [详情](https://gitee.com/hlc4417/cool-admin-java-plus) +- ...... +![](https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/flow.png) + +## 地址 + +- 官网:[https://cool-admin.com](https://cool-admin.com) +- 文档:[https://java.cool-admin.com](https://java.cool-admin.com) +- 商城项目:[https://cool-js.com/plugin/140](https://cool-js.com/plugin/140) +- Ai流程编排+知识库项目:[https://cool-js.com/plugin/118](https://cool-js.com/plugin/118) +- cool-admin-java-plus:https://gitee.com/hlc4417/cool-admin-java-plus + +## 演示 + +[https://show.cool-admin.com](https://show.cool-admin.com) + +- 账户:admin +- 密码:123456 + +![](https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/home-mini.png) + +#### 项目前端 + +系统是前后端分离的,启动完成后,还需要启动前端项目,前端项目地址: + +[https://github.com/cool-team-official/cool-admin-vue](https://github.com/cool-team-official/cool-admin-vue) + +或 + +[https://gitee.com/cool-team-official/cool-admin-vue](https://gitee.com/cool-team-official/cool-admin-vue) + +或 + +[https://gitcode.com/cool_team/cool-admin-vue](https://gitcode.com/cool_team/cool-admin-vue) + +## 微信群 + +Admin Wechat + +## 运行 + +### 环境要求 + +- Java Graalvm 17+ +- Maven 3.6+ + +### 配置 + +修改数据库配置,配置文件位于`src/resources/application-local.yml` + +以 Mysql 为例,其他数据库适配中... + +Mysql(`>=5.7版本`),建议 8.0,首次启动会自动初始化并导入数据 + +```yaml +# mysql,驱动已经内置,无需安装 +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/cool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8 + username: root + password: 123456 + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +### 启动 + +注:项目使用到了[Mybatis-Flex 的Apt功能](https://mybatis-flex.com/zh/others/apt.html),如果启动报错,请先执行`mvn compile`编译 + +1、启动文件:`src/main/java/com/cool/CoolApplication.java` + +2、启动完成后,访问:[http://localhost:8001](http://localhost:8001) + +3、如果看到以下界面,说明启动成功。这时候再启动前端项目即可,数据库会自动初始化,默认账号:admin,密码:123456 + +![](https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/run.png) diff --git a/cool-admin-java/docker-compose.yml b/cool-admin-java/docker-compose.yml new file mode 100644 index 0000000..7c9130b --- /dev/null +++ b/cool-admin-java/docker-compose.yml @@ -0,0 +1,43 @@ +# 本地数据库环境 +# 数据存放在当前目录下的 data里 +# 推荐使用安装了docker扩展的vscode打开目录 在本文件上右键可以快速启动,停止 +# 如不需要相关容器开机自启动,可注释掉 restart: always +# 如遇端口冲突 可调整ports下 :前面的端口号 +version: "3.9" + +services: + mysql: + image: mysql # 使用官方 MySQL 镜像,你可以根据需要选择版本 + environment: + MYSQL_ROOT_PASSWORD: "123456" # 设置 root 用户密码 + MYSQL_DATABASE: "cool" # 创建一个初始数据库 + networks: + - backend + ports: + - "3306:3306" # 将主机的 3306 端口映射到容器的 3306 端口 + volumes: + - mysql-data:/var/lib/mysql # 挂载数据卷以持久化数据 + + redis: + image: redis:latest + # command: --requirepass "12345678" # Uncomment if you need a password + restart: always + environment: + TZ: Asia/Shanghai # 指定时区 + volumes: + - ./data/redis/:/data/ + networks: + - backend + ports: + - 6379:6379 + + +networks: + backend: + driver: bridge + +volumes: + mysql-data: + driver: local + redis-data: + driver: local \ No newline at end of file diff --git a/cool-admin-java/pom.xml b/cool-admin-java/pom.xml new file mode 100644 index 0000000..bb33e1a --- /dev/null +++ b/cool-admin-java/pom.xml @@ -0,0 +1,213 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + com.cool + cool-admin + 8.0.0 + cool-admin + cool admin for java + + 17 + 1.18.34 + 1.10.9 + 1.10.9.125 + 5.8.26 + 3.3.2 + 2.0.51 + 2.5.0 + 0.9.16 + 4.7.0 + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-quartz + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter-aop + + + + com.github.ben-manes.caffeine + caffeine + + + com.mysql + mysql-connector-j + runtime + + + org.postgresql + postgresql + + + com.zaxxer + HikariCP + + + org.projectlombok + lombok + true + + + cn.hutool + hutool-all + ${hutool.version} + + + ognl + ognl + ${ognl.version} + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + + + + com.tangzc + mybatis-flex-ext-spring-boot3-starter + ${mybatis-flex.ext.version} + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc-openapi.version} + + + + com.github.binarywang + weixin-java-miniapp + ${weixin-java.version} + + + com.github.binarywang + weixin-java-mp + ${weixin-java.version} + + + com.github.binarywang + weixin-java-pay + ${weixin-java.version} + + + + jakarta.servlet + jakarta.servlet-api + provided + + + com.fasterxml.jackson.core + jackson-databind + + + org.perf4j + perf4j + ${perf4j.version} + + + + + + + org.graalvm.buildtools + native-maven-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + com.mybatis-flex + mybatis-flex-processor + ${mybatis-flex.version} + + + + + + + + + + local + + true + + + local + + + + prod + + prod + + + + + \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/CoolApplication.java b/cool-admin-java/src/main/java/com/cool/CoolApplication.java new file mode 100644 index 0000000..b5fac06 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/CoolApplication.java @@ -0,0 +1,74 @@ +package com.cool; + +import com.cool.core.util.PathUtils; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.dromara.autotable.springboot.EnableAutoTable; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * CoolApplication - 应用程序的主类 + * 该类配置并运行应用程序。 + */ +@Slf4j +@EnableAutoTable // 开启自动建表 +@EnableAsync // 开启异步处理 +@EnableCaching // 开启缓存 +@SpringBootApplication +@MapperScan("com.cool.**.mapper") // 扫描指定包中的MyBatis映射器 +public class CoolApplication { + + private static volatile ConfigurableApplicationContext context; + private static ClassLoader mainThreadClassLoader; + + public static void main(String[] args) { + mainThreadClassLoader = Thread.currentThread().getContextClassLoader(); + context = SpringApplication.run(CoolApplication.class, args); + } + + /** + * 通过关闭当前上下文并启动新上下文来重启应用程序。 + */ + public static void restart(List javaPathList) { + // 从当前上下文获取应用程序参数 + ApplicationArguments args = context.getBean(ApplicationArguments.class); + + // 创建新线程来重启应用程序 + Thread thread = new Thread(() -> { + try { + // 关闭当前应用程序上下文 + context.close(); + // 等待上下文完全关闭 + while (context.isActive()) { + Thread.sleep(100); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // 加载动态生成的代码 + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + javaPathList.forEach(javaPath -> { + try { + classLoader.loadClass(PathUtils.getClassName(javaPath)); + } catch (ClassNotFoundException e) { + log.error("loadClassErr {}", javaPath, e); + } + }); + // 使用相同的参数运行Spring Boot应用程序并设置上下文 + context = SpringApplication.run(CoolApplication.class, args.getSourceArgs()); + }); + // 设置线程的上下文类加载器 + thread.setContextClassLoader(mainThreadClassLoader); + // 确保线程不是守护线程 + thread.setDaemon(false); + // 启动线程 + thread.start(); + } + +} diff --git a/cool-admin-java/src/main/java/com/cool/Welcome.java b/cool-admin-java/src/main/java/com/cool/Welcome.java new file mode 100644 index 0000000..e34071a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/Welcome.java @@ -0,0 +1,17 @@ +package com.cool; + +import com.cool.core.annotation.TokenIgnore; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequiredArgsConstructor +public class Welcome { + + @RequestMapping("/") + @TokenIgnore + public String welcome() { + return "welcome"; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/CoolPlugin.java b/cool-admin-java/src/main/java/com/cool/core/annotation/CoolPlugin.java new file mode 100644 index 0000000..a010335 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/CoolPlugin.java @@ -0,0 +1,12 @@ +package com.cool.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface CoolPlugin { + String value() default ""; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/CoolRestController.java b/cool-admin-java/src/main/java/com/cool/core/annotation/CoolRestController.java new file mode 100644 index 0000000..c13be69 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/CoolRestController.java @@ -0,0 +1,34 @@ +package com.cool.core.annotation; + +import java.lang.annotation.*; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 自定义路由注解 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RestController +@RequestMapping +public @interface CoolRestController { + + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + String[] api() default {}; + + /** + * 如前缀: /admin/goods/searchKeyword + * 没指定该字段 cname="searchKeyword", + * 按规则是解析为: /admin/goods/search/keyword + * 前端和node版本已经定义为 searchKeyword,没按规则解析,使用该字段自定义规则 进行兼容 + * com.cool.core.request.prefix.AutoPrefixUrlMapping#getCName(java.lang.Class, java.lang.String) + */ + String cname() default ""; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/EpsField.java b/cool-admin-java/src/main/java/com/cool/core/annotation/EpsField.java new file mode 100644 index 0000000..1e59245 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/EpsField.java @@ -0,0 +1,14 @@ +package com.cool.core.annotation; + +import com.cool.core.enums.AdminComponentsEnum; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EpsField { + String component() default AdminComponentsEnum.INPUT; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/IgnoreRecycleData.java b/cool-admin-java/src/main/java/com/cool/core/annotation/IgnoreRecycleData.java new file mode 100644 index 0000000..6ceb893 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/IgnoreRecycleData.java @@ -0,0 +1,12 @@ +package com.cool.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface IgnoreRecycleData { + String value() default ""; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/NoRepeatSubmit.java b/cool-admin-java/src/main/java/com/cool/core/annotation/NoRepeatSubmit.java new file mode 100644 index 0000000..818c473 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/NoRepeatSubmit.java @@ -0,0 +1,12 @@ +package com.cool.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface NoRepeatSubmit { + long expireTime() default 2000; // 默认2秒过期时间,单位毫秒 +} diff --git a/cool-admin-java/src/main/java/com/cool/core/annotation/TokenIgnore.java b/cool-admin-java/src/main/java/com/cool/core/annotation/TokenIgnore.java new file mode 100644 index 0000000..b2d46aa --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/annotation/TokenIgnore.java @@ -0,0 +1,15 @@ +package com.cool.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 忽略Token验证 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TokenIgnore { + String[] value() default {}; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java b/cool-admin-java/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java new file mode 100644 index 0000000..501b771 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java @@ -0,0 +1,39 @@ +package com.cool.core.aop; + +import com.cool.core.annotation.NoRepeatSubmit; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.lock.CoolLock; +import com.cool.core.util.CoolSecurityUtil; +import jakarta.servlet.http.HttpServletRequest; +import java.time.Duration; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +@Aspect +@Component +@RequiredArgsConstructor +public class NoRepeatSubmitAspect { + + private final CoolLock coolLock; + + @Around("@annotation(noRepeatSubmit)") + public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Throwable { + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull( + RequestContextHolder.getRequestAttributes())).getRequest(); + String key = request.getRequestURI() + ":" + CoolSecurityUtil.getCurrentUserId(); + // 加锁 + CoolPreconditions.check(!coolLock.tryLock(key, Duration.ofMillis(noRepeatSubmit.expireTime())), "请勿重复操作"); + try { + return joinPoint.proceed(); + } finally { + // 移除锁 + coolLock.unlock(key); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/base/BaseController.java b/cool-admin-java/src/main/java/com/cool/core/base/BaseController.java new file mode 100644 index 0000000..32fc3f2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/BaseController.java @@ -0,0 +1,277 @@ +package com.cool.core.base; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.lang.Editor; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.TypeUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.enums.QueryModeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.request.CrudOption; +import com.cool.core.request.PageResult; +import com.cool.core.request.R; +import com.mybatisflex.core.paginate.Page; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * 控制层基类 + * + * @param + * @param + */ +public abstract class BaseController, T extends BaseEntity> { + + @Getter + @Autowired + protected S service; + protected Class entityClass; + + protected final String COOL_PAGE_OP = "COOL_PAGE_OP"; + protected final String COOL_LIST_OP = "COOL_LIST_OP"; + protected final String COOL_INFO_OP = "COOL_INFO_OP"; + + private final ThreadLocal> pageOption = new ThreadLocal<>(); + private final ThreadLocal> listOption = new ThreadLocal<>(); + private final ThreadLocal> infoOption = new ThreadLocal<>(); + private final ThreadLocal requestParams = new ThreadLocal<>(); + + @ModelAttribute + protected void preHandle(HttpServletRequest request, + @RequestAttribute JSONObject requestParams) { + String requestPath = ((ServletRequestAttributes) Objects.requireNonNull( + RequestContextHolder.getRequestAttributes())).getRequest().getRequestURI(); + if (!requestPath.endsWith("/page") && !requestPath.endsWith("/list") + && !requestPath.endsWith("/info")) { + // 非page或list不执行 + return; + } + this.pageOption.set(new CrudOption<>(requestParams)); + this.listOption.set(new CrudOption<>(requestParams)); + this.infoOption.set(new CrudOption<>(requestParams)); + this.requestParams.set(requestParams); + init(request, requestParams); + request.setAttribute(COOL_PAGE_OP, this.pageOption.get()); + request.setAttribute(COOL_LIST_OP, this.listOption.get()); + request.setAttribute(COOL_INFO_OP, this.infoOption.get()); + + removeThreadLocal(); + } + + /** + * 手动移除变量 + */ + private void removeThreadLocal() { + this.listOption.remove(); + this.pageOption.remove(); + this.requestParams.remove(); + } + + public CrudOption createOp() { + return new CrudOption<>(this.requestParams.get()); + } + + public void setInfoOption(CrudOption infoOption) { + this.infoOption.set(infoOption); + } + + public void setListOption(CrudOption listOption) { + this.listOption.set(listOption); + } + + public void setPageOption(CrudOption pageOption) { + this.pageOption.set(pageOption); + } + + protected abstract void init(HttpServletRequest request, JSONObject requestParams); + + /** + * 新增 + *

+ * // * @param t 实体对象 + */ + @Operation(summary = "新增", description = "新增信息,对应后端的实体类") + @PostMapping("/add") + protected R add(@RequestAttribute() JSONObject requestParams) { + String body = requestParams.getStr("body"); + if (JSONUtil.isTypeJSONArray(body)) { + JSONArray array = JSONUtil.parseArray(body); + return R.ok(Dict.create() + .set("id", service.addBatch(requestParams, array.toList(currentEntityClass())))); + } else { + return R.ok(Dict.create().set("id", + service.add(requestParams, requestParams.toBean(currentEntityClass())))); + } + } + + /** + * 删除 + * + * @param params 请求参数 ids 数组 或者按","隔开 + */ + @Operation(summary = "删除", description = "支持批量删除 请求参数 ids 数组 或者按\",\"隔开") + @PostMapping("/delete") + protected R delete(HttpServletRequest request, @RequestBody Map params, + @RequestAttribute() JSONObject requestParams) { + service.delete(requestParams, Convert.toLongArray(getIds(params))); + return R.ok(); + } + + /** + * 修改 + * + * @param t 修改对象 + */ + @Operation(summary = "修改", description = "根据ID修改") + @PostMapping("/update") + protected R update(@RequestBody T t, @RequestAttribute() JSONObject requestParams) { + Long id = t.getId(); + JSONObject info = JSONUtil.parseObj(JSONUtil.toJsonStr(service.getById(id))); + requestParams.forEach(info::set); + info.set("updateTime", new Date()); + service.update(requestParams, JSONUtil.toBean(info, currentEntityClass())); + return R.ok(); + } + + /** + * 信息 + * + * @param id ID + */ + @Operation(summary = "信息", description = "根据ID查询单个信息") + @GetMapping("/info") + protected R info(@RequestAttribute() JSONObject requestParams, + @RequestParam() Long id, + @RequestAttribute(COOL_INFO_OP) CrudOption option) { + T info = (T) service.info(requestParams, id); + invokerTransform(option, info); + return R.ok(info); + } + + /** + * 列表查询 + * + * @param requestParams 请求参数 + */ + @Operation(summary = "查询", description = "查询多个信息") + @PostMapping("/list") + protected R> list(@RequestAttribute() JSONObject requestParams, + @RequestAttribute(COOL_LIST_OP) CrudOption option) { + QueryModeEnum queryModeEnum = option.getQueryModeEnum(); + List list = (List) switch (queryModeEnum) { + case ENTITY_WITH_RELATIONS -> service.listWithRelations(requestParams, option.getQueryWrapper(currentEntityClass())); + case CUSTOM -> transformList(service.list(requestParams, option.getQueryWrapper(currentEntityClass()), option.getAsType()), option.getAsType()); + default -> service.list(requestParams, option.getQueryWrapper(currentEntityClass())); + }; + invokerTransform(option, list); + return R.ok(list); + } + + /** + * 分页查询 + * + * @param requestParams 请求参数 + */ + @Operation(summary = "分页", description = "分页查询多个信息") + @PostMapping("/page") + protected R> page(@RequestAttribute() JSONObject requestParams, + @RequestAttribute(COOL_PAGE_OP) CrudOption option) { + Integer page = requestParams.getInt("page", 1); + Integer size = requestParams.getInt("size", 20); + QueryModeEnum queryModeEnum = option.getQueryModeEnum(); + Object obj = switch (queryModeEnum) { + case ENTITY_WITH_RELATIONS -> service.pageWithRelations(requestParams, new Page<>(page, size), option.getQueryWrapper(currentEntityClass())); + case CUSTOM -> transformPage(service.page(requestParams, new Page<>(page, size), option.getQueryWrapper(currentEntityClass()), option.getAsType()), option.getAsType()); + default -> service.page(requestParams, new Page<>(page, size), option.getQueryWrapper(currentEntityClass())); + }; + Page pageResult = (Page) obj; + invokerTransform(option, pageResult.getRecords()); + return R.ok(pageResult(pageResult)); + } + + /** + * 转换参数,组装数据 + */ + private void invokerTransform(CrudOption option, Object obj) { + if (ObjUtil.isNotEmpty(option.getTransform())) { + if (obj instanceof List) { + ((List)obj).forEach(o -> { + option.getTransform().apply(o); + }); + } else { + option.getTransform().apply(obj); + } + } + } + + /** + * 分页结果 + * + * @param page 分页返回数据 + */ + protected PageResult pageResult(Page page) { + return PageResult.of(page); + } + + public Class currentEntityClass() { + if (entityClass != null) { + return this.entityClass; + } + // 使用 获取泛型参数类型 + Type type = TypeUtil.getTypeArgument(this.getClass(), 1); // 获取第二个泛型参数 + if (type instanceof Class) { + entityClass = (Class) type; + return entityClass; + } + throw new IllegalStateException("Unable to determine entity class type"); + } + + protected List getIds(Map params) { + Object ids = params.get("ids"); + CoolPreconditions.checkEmpty(ids, "ids 参数错误"); + if (!(ids instanceof ArrayList)) { + ids = ids.toString().split(","); + } + return Convert.toList(Long.class, ids); + } + + /** + * 适用于自定义返回值为 map,map 的key为数据库字段,转驼峰命名 + */ + protected List transformList(List records, Class asType) { + if (ObjUtil.isEmpty(asType) || !Map.class.isAssignableFrom(asType)) { + return records; + } + List list = new ArrayList<>(); + Editor keyEditor = property -> StrUtil.toCamelCase(property); + records.forEach(o -> + list.add(BeanUtil.beanToMap(o, new HashMap(), false, keyEditor))); + return list; + } + protected Page transformPage(Page page, Class asType) { + page.setRecords(transformList(page.getRecords(), asType)); + return page; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/base/BaseEntity.java b/cool-admin-java/src/main/java/com/cool/core/base/BaseEntity.java new file mode 100644 index 0000000..08f153f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/BaseEntity.java @@ -0,0 +1,38 @@ +package com.cool.core.base; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.core.activerecord.Model; +import com.mybatisflex.core.query.QueryWrapper; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.io.Serializable; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Ignore; + +/** + * 基础实体类 + */ +@Getter +@Setter +public abstract class BaseEntity> extends Model implements Serializable { + + @Id(keyType = KeyType.Auto, comment = "ID") + protected Long id; + + @Column(onInsertValue = "now()") + @ColumnDefine(comment = "创建时间") + protected Date createTime; + + @Column(onInsertValue = "now()", onUpdateValue = "now()") + @ColumnDefine(comment = "更新时间") + protected Date updateTime; + + @Ignore + @Column(ignore = true) + @JsonIgnore + private QueryWrapper queryWrapper; +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/base/BaseService.java b/cool-admin-java/src/main/java/com/cool/core/base/BaseService.java new file mode 100644 index 0000000..dff2fda --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/BaseService.java @@ -0,0 +1,175 @@ +package com.cool.core.base; + +import cn.hutool.json.JSONObject; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.service.IService; +import java.util.List; + +/** + * 基础service类 + * + * @param 实体 + */ +public interface BaseService extends IService { + /** + * 新增 + * + * @param entity 对应的实体 + */ + Long add(T entity); + + /** + * 新增 + * + * @param requestParams 请求参数 + * @param entity 对应的实体 + * @return ID + */ + Object add(JSONObject requestParams, T entity); + + /** + * 批量添加 + * + * @param requestParams 请求参数 + * @param entitys 请求参数 + * @return ID 集合 + */ + Object addBatch(JSONObject requestParams, List entitys); + + /** + * 删除, 支持单个或者批量删除 + * + * @param ids ID数组 + */ + boolean delete(Long... ids); + + /** + * 多个删除,带请求参数 + * + * @param requestParams 请求参数 + * @param ids ID数组 + */ + boolean delete(JSONObject requestParams, Long... ids); + + /** + * 更新 + * + * @param entity 实体 + */ + boolean update(T entity); + + /** + * 更新 + * + * @param requestParams 请求参数 + * @param entity 实体 + */ + boolean update(JSONObject requestParams, T entity); + + /** + * 查询所有 + * + * @param requestParams 请求参数 + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + Object list(JSONObject requestParams, QueryWrapper queryWrapper); + + /** + * 查询所有 + * + * @param requestParams 请求参数 + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + List list(JSONObject requestParams, QueryWrapper queryWrapper, Class asType); + + /** + * 查询所有 + * 带关联查询 + * @param requestParams 请求参数 + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + Object listWithRelations(JSONObject requestParams, QueryWrapper queryWrapper); + + /** + * 分页查询 + * + * @param requestParams 请求参数 + * @param page 分页信息 + * @param queryWrapper 查询条件 + * @return 分页信息 + */ + Object page(JSONObject requestParams, Page page, QueryWrapper queryWrapper); + + /** + * 分页查询 + * + * @param requestParams 请求参数 + * @param page 分页信息 + * @param queryWrapper 查询条件 + * @return 分页信息 + */ + Page page(JSONObject requestParams, Page page, QueryWrapper queryWrapper, Class asType); + + /** + * 分页查询 + * 带关联查询 + * @param requestParams 请求参数 + * @param page 分页信息 + * @param queryWrapper 查询条件 + * @return 分页信息 + */ + Object pageWithRelations(JSONObject requestParams, Page page, QueryWrapper queryWrapper); + + /** + * 查询信息 + * + * @param id ID + */ + Object info(Long id); + + /** + * 查询信息 + * + * @param requestParams 请求参数 + * @param id ID + */ + Object info(JSONObject requestParams, Long id); + + /** + * 修改之后 + * + * @param requestParams 请求参数 + * @param t 对应实体 + */ + void modifyAfter(JSONObject requestParams, T t); + + /** + * 修改之后 + * + * @param requestParams 请求参数 + * @param t 对应实体 + * @param type 修改类型 + */ + void modifyAfter(JSONObject requestParams, T t, ModifyEnum type); + + /** + * 修改之前 + * + * @param requestParams 请求参数 + * @param t 对应实体 + */ + void modifyBefore(JSONObject requestParams, T t); + + /** + * 修改之前 + * + * @param requestParams 请求参数 + * @param t 对应实体 + * @param type 修改类型 + */ + void modifyBefore(JSONObject requestParams, T t, ModifyEnum type); +} diff --git a/cool-admin-java/src/main/java/com/cool/core/base/BaseServiceImpl.java b/cool-admin-java/src/main/java/com/cool/core/base/BaseServiceImpl.java new file mode 100644 index 0000000..f4380cc --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/BaseServiceImpl.java @@ -0,0 +1,137 @@ +package com.cool.core.base; + +import cn.hutool.json.JSONObject; +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 基础service实现类 + * + * @param Mapper 类 + * @param 实体 + */ +public class BaseServiceImpl, T extends BaseEntity> extends + ServiceImpl + implements BaseService { + + @Override + public Long add(T entity) { + mapper.insertSelective(entity); + return entity.getId(); + } + + @Override + public Object add(JSONObject requestParams, T entity) { + this.modifyBefore(requestParams, entity, ModifyEnum.ADD); + this.add(entity); + this.modifyAfter(requestParams, entity, ModifyEnum.ADD); + return entity.getId(); + } + + @Override + public Object addBatch(JSONObject requestParams, List entitys) { + this.modifyBefore(requestParams, null, ModifyEnum.ADD); + List ids = new ArrayList<>(); + entitys.forEach(e -> ids.add(this.add(e))); + requestParams.set("ids", ids); + this.modifyAfter(requestParams, null, ModifyEnum.ADD); + return ids; + } + + @Override + public boolean delete(Long... ids) { + return mapper.deleteBatchByIds(Arrays.asList(ids)) > 0; + } + + @Override + public boolean delete(JSONObject requestParams, Long... ids) { + this.modifyBefore(requestParams, null, ModifyEnum.DELETE); + boolean flag = this.delete(ids); + if (flag) { + this.modifyAfter(requestParams, null, ModifyEnum.DELETE); + } + return flag; + } + + @Override + public boolean update(T entity) { + return mapper.update(entity) > 0; + } + + @Override + public boolean update(JSONObject requestParams, T entity) { + this.modifyBefore(requestParams, entity, ModifyEnum.UPDATE); + boolean flag = this.update(entity); + if (flag) { + this.modifyAfter(requestParams, entity, ModifyEnum.UPDATE); + } + return flag; + } + + @Override + public Object list(JSONObject requestParams, QueryWrapper queryWrapper) { + return this.list(queryWrapper); + } + + @Override + public List list(JSONObject requestParams, QueryWrapper queryWrapper, Class asType) { + return mapper.selectListByQueryAs(queryWrapper, asType); + } + + @Override + public Object listWithRelations(JSONObject requestParams, QueryWrapper queryWrapper) { + return mapper.selectListWithRelationsByQuery(queryWrapper); + } + + @Override + public Object page(JSONObject requestParams, Page page, QueryWrapper queryWrapper) { + return this.page(page, queryWrapper); + } + + @Override + public Page page(JSONObject requestParams, Page page, QueryWrapper queryWrapper, + Class asType) { + return mapper.paginateAs(page, queryWrapper, asType); + } + + @Override + public Object pageWithRelations(JSONObject requestParams, Page page, + QueryWrapper queryWrapper) { + return mapper.paginateWithRelations(page, queryWrapper); + } + + @Override + public Object info(JSONObject requestParams, Long id) { + return info(id); + } + + @Override + public Object info(Long id) { + return mapper.selectOneById(id); + } + + @Override + public void modifyAfter(JSONObject requestParams, T t) { + + } + + @Override + public void modifyAfter(JSONObject requestParams, T t, ModifyEnum type) { + modifyAfter(requestParams, t); + } + + @Override + public void modifyBefore(JSONObject requestParams, T t) { + + } + + @Override + public void modifyBefore(JSONObject requestParams, T t, ModifyEnum type) { + + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/base/ModifyEnum.java b/cool-admin-java/src/main/java/com/cool/core/base/ModifyEnum.java new file mode 100644 index 0000000..22b0d85 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/ModifyEnum.java @@ -0,0 +1,13 @@ +package com.cool.core.base; + +/** + * 修改枚举 + */ +public enum ModifyEnum { + // 新增 + ADD, + // 修改 + UPDATE, + // 删除 + DELETE +} diff --git a/cool-admin-java/src/main/java/com/cool/core/base/TenantEntity.java b/cool-admin-java/src/main/java/com/cool/core/base/TenantEntity.java new file mode 100644 index 0000000..4e258d5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/TenantEntity.java @@ -0,0 +1,16 @@ +package com.cool.core.base; + +import com.mybatisflex.core.activerecord.Model; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +/** 租户ID实体类 */ +@Getter +@Setter +public class TenantEntity> extends BaseEntity { + @Index + @ColumnDefine(comment = "租户id") + protected Long tenantId; +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/base/service/MapperProviderService.java b/cool-admin-java/src/main/java/com/cool/core/base/service/MapperProviderService.java new file mode 100644 index 0000000..8492857 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/base/service/MapperProviderService.java @@ -0,0 +1,52 @@ +package com.cool.core.base.service; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.TypeUtil; +import com.cool.core.util.SpringContextUtils; +import com.mybatisflex.core.BaseMapper; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.springframework.stereotype.Service; + +@Service +public class MapperProviderService { + + private Map, BaseMapper> mapperMap; + + /** + * 初始化mapperMap,key 为entityClass,value 为 mapper + */ + private void init() { + // 获取所有BaseMapper类型的Bean + Map beansOfType = SpringContextUtils.getBeansOfType(BaseMapper.class); + mapperMap = new HashMap<>(); + for (BaseMapper mapper : beansOfType.values()) { + // 通过反射获取泛型参数,即实体类 + Class entityClass = getGenericType(mapper); + if (entityClass != null) { + mapperMap.put(entityClass, mapper); + } + } + } + + /** + * 通过entity类获取 对应的mapper接口 + */ + public BaseMapper getMapperByEntityClass(Class entityClass) { + if (ObjUtil.isEmpty(mapperMap)) { + init(); + } + return (BaseMapper) mapperMap.get(entityClass); + } + + /** + * 获取mapper对应的entity对象 + */ + private Class getGenericType(BaseMapper mapper) { + // 使用 获取泛型参数类型 + Type[] types = mapper.getClass().getGenericInterfaces(); + Type typeArgument = TypeUtil.getTypeArgument(types[0], 0); + return ObjUtil.isEmpty(typeArgument) ? null : (Class) typeArgument; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/cache/CoolCache.java b/cool-admin-java/src/main/java/com/cool/core/cache/CoolCache.java new file mode 100644 index 0000000..aa6d381 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/cache/CoolCache.java @@ -0,0 +1,180 @@ +package com.cool.core.cache; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.cool.core.util.ConvertUtil; +import jakarta.annotation.PostConstruct; +import java.time.Duration; +import java.util.Arrays; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.cache.CacheType; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.stereotype.Component; + +/** + * 缓存工具类 + */ +@EnableCaching +@Configuration +@Component +@RequiredArgsConstructor +public class CoolCache { + + // 缓存类型 + @Value("${spring.cache.type}") + private String type; + + // redis + public RedisCacheWriter redisCache; + + private Cache cache; + + @Value("${cool.cacheName}") + private String cacheName; + + private final static String NULL_VALUE = "@_NULL_VALUE$@"; + + final private CacheManager cacheManager; + + @PostConstruct + private void init() { + cache = cacheManager.getCache(cacheName); + this.type = type.toLowerCase(); + assert cache != null : "Cache not found: " + cacheName; // Ensure cache is not null + if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + redisCache = (RedisCacheWriter) cache.getNativeCache(); + } + } + + /** + * 数据来源 + */ + public interface ToCacheData { + Object apply(); + } + + /** + * 删除缓存 + * + * @param keys 一个或多个key + */ + public void del(String... keys) { + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Arrays.stream(keys).forEach(o -> cache.evict(o)); + } + if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + Arrays.stream(keys).forEach(key -> redisCache.remove(cacheName, key.getBytes())); + } + } + + /** + * 普通缓存获取 + * + * @param key 键 + */ + public Object get(String key) { + Object ifNullValue = getIfNullValue(key); + if (ObjUtil.equals(ifNullValue, NULL_VALUE)) { + return null; + } + return ifNullValue; + } + + /** + * 普通缓存获取 + * + * @param key 键 + */ + public Object get(String key, Duration duration, ToCacheData toCacheData) { + Object ifNullValue = getIfNullValue(key); + if (ObjUtil.equals(ifNullValue, NULL_VALUE)) { + return null; + } + if (ObjUtil.isEmpty(ifNullValue)) { + Object obj = toCacheData.apply(); + set(key, obj, duration.toSeconds()); + return obj; + } + return ifNullValue; + } + + private Object getIfNullValue(String key) { + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Cache.ValueWrapper valueWrapper = cache.get(key); + if (valueWrapper != null) { + return valueWrapper.get(); // 获取实际的缓存值 + } + } + if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + byte[] bytes = redisCache.get(cacheName, key.getBytes()); + if (bytes != null && bytes.length > 0) { + return ConvertUtil.toObject(bytes); + } + } + return null; + } + + /** + * 获得对象 + * + * @param key 键 + * @param valueType 值类型 + */ + public T get(String key, Class valueType) { + Object result = get(key); + if (result != null && JSONUtil.isTypeJSONObject(result.toString())) { + return JSONUtil.parseObj(result).toBean(valueType); + } + return result != null ? (T) result : null; + } + + /** + * 获得缓存类型 + */ + public String getMode() { + return this.type; + } + + /** + * 获得原生缓存实例 + */ + public Object getMetaCache() { + return this.cache; + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + */ + public void set(String key, Object value) { + set(key, value, 0); + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param ttl 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + */ + public void set(String key, Object value, long ttl) { + if (ObjUtil.isNull(value)) { + value = NULL_VALUE; + } + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + // 放入缓存 + cache.put(key, value); + } else if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + redisCache.put(cacheName, key.getBytes(), ObjectUtil.serialize(value), + java.time.Duration.ofSeconds(ttl)); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/code/CodeGenerator.java b/cool-admin-java/src/main/java/com/cool/core/code/CodeGenerator.java new file mode 100644 index 0000000..37a8400 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/code/CodeGenerator.java @@ -0,0 +1,108 @@ +package com.cool.core.code; + +import cn.hutool.core.io.file.FileWriter; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.template.Template; +import cn.hutool.extra.template.TemplateConfig; +import cn.hutool.extra.template.TemplateEngine; +import cn.hutool.extra.template.TemplateUtil; +import jakarta.annotation.PostConstruct; +import org.springframework.stereotype.Component; + +/** + * 代码生成器 + */ +@Component +public class CodeGenerator { + + private TemplateEngine templateEngine; + + private String baseSrcPath; + + private String baseResPath; + + @PostConstruct + public void init() { + templateEngine = coolTemplateEngine(); + baseSrcPath = System.getProperty("user.dir") + "/src/main/java/com/cool/modules/"; + baseResPath = System.getProperty("user.dir") + "/src/main/resources/"; + } + + public TemplateEngine coolTemplateEngine() { + return TemplateUtil.createEngine( + new TemplateConfig("cool/code", TemplateConfig.ResourceMode.CLASSPATH)); + } + + private String filePath(CodeModel codeModel, String type) { + if (type.equals("controller")) { + return StrUtil.isEmpty(codeModel.getSubModule()) + ? baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getType() + .value() + : baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getType() + .value() + "/" + + codeModel.getSubModule(); + } + if (type.equals("xmlMapper")) { + return StrUtil.isEmpty(codeModel.getSubModule()) ? baseResPath + "mapper/" + + codeModel.getModule() + : baseResPath + "mapper/" + codeModel.getModule() + "/" + codeModel.getSubModule(); + } + return StrUtil.isEmpty(codeModel.getSubModule()) ? baseSrcPath + codeModel.getModule() + "/" + + type + : baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getSubModule(); + } + + /** + * 生成Mapper + * + * @param codeModel 代码模型 + */ + public void mapper(CodeModel codeModel) { + Template template = templateEngine.getTemplate("/mapper/interface.th"); + String result = template.render(Dict.parse(codeModel)); + FileWriter writer = new FileWriter( + filePath(codeModel, "mapper") + "/" + codeModel.getEntity() + "Mapper.java"); + writer.write(result); + } + + /** + * 生成Service + * + * @param codeModel 代码模型 + */ + public void service(CodeModel codeModel) { + Template interfaceTemplate = templateEngine.getTemplate("/service/interface.th"); + String interfaceResult = interfaceTemplate.render(Dict.parse(codeModel)); + FileWriter interfaceWriter = new FileWriter( + filePath(codeModel, "service") + "/" + codeModel.getEntity() + "Service.java"); + interfaceWriter.write(interfaceResult); + + Template template = templateEngine.getTemplate("/service/impl.th"); + String result = template.render(Dict.parse(codeModel)); + FileWriter writer = new FileWriter( + filePath(codeModel, "service") + "/impl/" + codeModel.getEntity() + "ServiceImpl.java"); + writer.write(result); + } + + /** + * 生成Controller + * + * @param codeModel 代码模型 + */ + public void controller(CodeModel codeModel) { + Template template = templateEngine.getTemplate("controller.th"); + System.out.println(codeModel.getType().value()); + Dict data = Dict.create().set("upperType", StrUtil.upperFirst(codeModel.getType().value())) + .set("url", + "/" + codeModel.getType() + "/" + StrUtil.toUnderlineCase(codeModel.getEntity()) + .replace("_", "/")); + data.putAll(Dict.parse(codeModel)); + data.set("type", codeModel.getType().value()); + String result = template.render(data); + FileWriter writer = new FileWriter(filePath(codeModel, "controller") + "/" + + StrUtil.upperFirst(codeModel.getType().value()) + codeModel.getEntity() + + "Controller.java"); + writer.write(result); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/code/CodeModel.java b/cool-admin-java/src/main/java/com/cool/core/code/CodeModel.java new file mode 100644 index 0000000..a2566fd --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/code/CodeModel.java @@ -0,0 +1,36 @@ +package com.cool.core.code; + +import lombok.Data; + +/** + * 代码模型 + */ +@Data +public class CodeModel { + /** + * 类型 后台还是对外的接口 admin app + */ + private CodeTypeEnum type; + /** + * 名称 + */ + private String name; + /** + * 模块 + */ + private String module; + + /** + * 子模块 + */ + private String subModule; + + /** + * 实体类 + */ + private String entity; + + public void setEntity(Class entity) { + this.entity = entity.getSimpleName().replace("Entity", ""); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/code/CodeTypeEnum.java b/cool-admin-java/src/main/java/com/cool/core/code/CodeTypeEnum.java new file mode 100644 index 0000000..873ef49 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/code/CodeTypeEnum.java @@ -0,0 +1,21 @@ +package com.cool.core.code; + +/** + * 代码类型 + */ +public enum CodeTypeEnum { + ADMIN("admin", "后端接口"), APP("app", "对外接口"); + + private String value; + + private String des; + + CodeTypeEnum(String value, String des) { + this.value = value; + this.des = des; + } + + public String value() { + return this.value; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/CoolProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/CoolProperties.java new file mode 100644 index 0000000..88e3fe9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/CoolProperties.java @@ -0,0 +1,23 @@ +package com.cool.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.context.annotation.Configuration; + +/** + * cool的配置 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "cool") +public class CoolProperties { + // 是否自动导入数据 + private Boolean initData = false; + // token配置 + @NestedConfigurationProperty + private TokenProperties token; + // 文件配置 + @NestedConfigurationProperty + private FileProperties file; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/CustomOpenApiResource.java b/cool-admin-java/src/main/java/com/cool/core/config/CustomOpenApiResource.java new file mode 100644 index 0000000..e01d325 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/CustomOpenApiResource.java @@ -0,0 +1,40 @@ +package com.cool.core.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Locale; +import org.springdoc.core.customizers.SpringDocCustomizers; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.providers.SpringDocProviders; +import org.springdoc.core.service.AbstractRequestService; +import org.springdoc.core.service.GenericResponseService; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.service.OperationService; +import org.springdoc.webmvc.api.OpenApiResource; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +/** + * 自定义 OpenApiResource + */ +@Component +@ConditionalOnProperty( + name = "springdoc.api-docs.enabled", + havingValue = "true" +) +public class CustomOpenApiResource extends OpenApiResource { + + public CustomOpenApiResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders, SpringDocCustomizers springDocCustomizers) { + super("springdocDefault", openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, springDocConfigProperties, springDocProviders, springDocCustomizers); + } + + @Override + protected String getServerUrl(HttpServletRequest request, String apiDocsUrl) { + return ""; + } + + public byte[] getOpenApiJson() throws JsonProcessingException { + return writeJsonValue(getOpenApi(Locale.getDefault())); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/config/FileModeEnum.java b/cool-admin-java/src/main/java/com/cool/core/config/FileModeEnum.java new file mode 100644 index 0000000..243520b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/FileModeEnum.java @@ -0,0 +1,28 @@ +package com.cool.core.config; + +/** + * 文件模式 + */ +public enum FileModeEnum { + LOCAL("local", "local", "本地"), CLOUD("cloud", "oss", "云存储"), OTHER("other", "other", "其他"); + + private String value; + + private String type; + + private String des; + + FileModeEnum(String value, String type, String des) { + this.value = value; + this.type = type; + this.des = des; + } + + public String value() { + return this.value; + } + + public String type() { + return this.type; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/FileProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/FileProperties.java new file mode 100644 index 0000000..4af01c0 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/FileProperties.java @@ -0,0 +1,22 @@ +package com.cool.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.context.annotation.Configuration; + +/** + * 文件 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "cool.file") +public class FileProperties { + // 上传模式 + private FileModeEnum mode; + // 上传类型 + private String type; + // 本地文件上传 + @NestedConfigurationProperty + private LocalFileProperties local; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/JacksonConfig.java b/cool-admin-java/src/main/java/com/cool/core/config/JacksonConfig.java new file mode 100644 index 0000000..8351306 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/JacksonConfig.java @@ -0,0 +1,67 @@ +package com.cool.core.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; +import java.io.IOException; +import java.math.BigInteger; +import java.text.SimpleDateFormat; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +@Configuration +public class JacksonConfig { + + @Bean + public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { + final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); + final ObjectMapper objectMapper = builder.build(); + SimpleModule simpleModule = new SimpleModule(); + // Long,BigInteger 转为 String 防止 js 丢失精度 + simpleModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); + simpleModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); + simpleModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); + objectMapper.registerModule(simpleModule); + // 配置日期格式为 yyyy-MM-dd HH:mm:ss + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + objectMapper.setDateFormat(dateFormat); + return new MappingJackson2HttpMessageConverter(objectMapper); + } + + /** + * 超出 JS 最大最小值 处理 + */ + @JacksonStdImpl + public static class BigNumberSerializer extends NumberSerializer { + + /** + * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 + */ + private static final long MAX_SAFE_INTEGER = 9007199254740991L; + private static final long MIN_SAFE_INTEGER = -9007199254740991L; + + /** + * 提供实例 + */ + public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 超出范围 序列化位字符串 + if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/config/LocalFileProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/LocalFileProperties.java new file mode 100644 index 0000000..5ecad6f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/LocalFileProperties.java @@ -0,0 +1,30 @@ +package com.cool.core.config; + +import com.cool.core.util.PathUtils; +import java.io.File; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 文件 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "cool.file.local") +public class LocalFileProperties { + + // 跟域名 + private String baseUrl; + + private String uploadPath = "assets/public/upload"; + + public String getAbsoluteUploadFolder() { + if (!PathUtils.isAbsolutePath(uploadPath)) { + // 相对路径 + return System.getProperty("user.dir") + File.separator + uploadPath; + } + // 绝对路径 + return uploadPath; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/LogDiscardPolicy.java b/cool-admin-java/src/main/java/com/cool/core/config/LogDiscardPolicy.java new file mode 100644 index 0000000..a661aaa --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/LogDiscardPolicy.java @@ -0,0 +1,13 @@ +package com.cool.core.config; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class LogDiscardPolicy implements RejectedExecutionHandler { + + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + log.warn("logTaskExecutor 当前已超过线程池最大队列容量,拒绝策略为丢弃该线程 {}", r.toString()); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/config/LogProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/LogProperties.java new file mode 100644 index 0000000..0452ff8 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/LogProperties.java @@ -0,0 +1,28 @@ +package com.cool.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "cool.log") +public class LogProperties { + + /** + * 请求参数最大字节,超过请求参数不记录 + */ + private int maxByteLength; + /** + * 核心线程数的倍数 + */ + private int corePoolSizeMultiplier; + /** + * 最大线程数的倍数 + */ + private int maxPoolSizeMultiplier; + /** + * 队列容量的倍数 + */ + private int queueCapacityMultiplier; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/MyBatisFlexConfiguration.java b/cool-admin-java/src/main/java/com/cool/core/config/MyBatisFlexConfiguration.java new file mode 100644 index 0000000..e7dcfa1 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/MyBatisFlexConfiguration.java @@ -0,0 +1,27 @@ +package com.cool.core.config; + +import com.cool.core.tenant.CoolTenantFactory; +import com.mybatisflex.core.FlexGlobalConfig; +import com.mybatisflex.core.tenant.TenantFactory; +import com.mybatisflex.spring.boot.MyBatisFlexCustomizer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyBatisFlexConfiguration implements MyBatisFlexCustomizer { + + @Override + public void customize(FlexGlobalConfig globalConfig) { + // 我们可以在这里进行一些列的初始化配置 + + // 指定多租户列的列名 + FlexGlobalConfig.getDefaultConfig().setTenantColumn("tenant_id"); + } + + @Bean + @ConditionalOnProperty(name = "cool.multi-tenant.enable", havingValue = "true") + public TenantFactory tenantFactory(){ + return new CoolTenantFactory(); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/config/OssFileProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/OssFileProperties.java new file mode 100644 index 0000000..1f02b3d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/OssFileProperties.java @@ -0,0 +1,24 @@ +package com.cool.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 文件 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "cool.file.oss") +public class OssFileProperties { + // accessKeyId + private String accessKeyId; + // accessKeySecret + private String accessKeySecret; + // 文件空间 + private String bucket; + // 地址 + private String endpoint; + // 超时时间 + private Long timeout; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/PluginJson.java b/cool-admin-java/src/main/java/com/cool/core/config/PluginJson.java new file mode 100644 index 0000000..ad39263 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/PluginJson.java @@ -0,0 +1,57 @@ +package com.cool.core.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.Map; + +@Data +public class PluginJson { + /** + * 插件名称 + */ + private String name; + /** + * 插件标识 + */ + private String key; + /** + * 插件钩子,比如替换系统的上传组件,upload + */ + private String hook; + /** + * 版本号 + */ + private String version; + /** + * 插件描述 + */ + private String description; + /** + * 作者 + */ + private String author; + /** + * 插件 logo,建议尺寸 256x256 + */ + private String logo; + /** + * 插件介绍,会展示在插件的详情中 + */ + private String readme; + /** + * 插件配置, 每个插件的配置各不相同 + */ + private Map config; + + /** + * jar包存放路径 + */ + private String jarPath; + + /** + * 同名hook id + */ + @JsonIgnore + private Long sameHookId; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/SwaggerConfig.java b/cool-admin-java/src/main/java/com/cool/core/config/SwaggerConfig.java new file mode 100644 index 0000000..8772d16 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/SwaggerConfig.java @@ -0,0 +1,16 @@ +package com.cool.core.config; + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@OpenAPIDefinition(info = @Info(title = "COOL-ADMIN", version = "4.0", description = "一个很酷的后台权限管理系统开发框架", contact = @Contact(name = "闪酷科技")), security = @SecurityRequirement(name = "Authorization"), externalDocs = @ExternalDocumentation(description = "参考文档", url = "https://cool-js.com")) +@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "Authorization", in = SecuritySchemeIn.HEADER) +public class SwaggerConfig { + +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/ThreadPoolConfig.java b/cool-admin-java/src/main/java/com/cool/core/config/ThreadPoolConfig.java new file mode 100644 index 0000000..9fc6dc7 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/ThreadPoolConfig.java @@ -0,0 +1,41 @@ +package com.cool.core.config; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@RequiredArgsConstructor +public class ThreadPoolConfig { + + private final LogProperties logProperties; + + @Bean(name = "logTaskExecutor") + public Executor loggingTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + int corePoolSize = Runtime.getRuntime().availableProcessors() * logProperties.getCorePoolSizeMultiplier(); + int maxPoolSize = corePoolSize * logProperties.getMaxPoolSizeMultiplier(); + int queueCapacity = maxPoolSize * logProperties.getQueueCapacityMultiplier(); + + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setThreadNamePrefix("logTask-"); + + // 自定义拒绝策略 + executor.setRejectedExecutionHandler(new LogDiscardPolicy()); + executor.initialize(); + return executor; + } + + @Bean(name = "cachedThreadPool") + public ExecutorService cachedThreadPool() { + // 创建一个虚拟线程池,每个任务使用一个虚拟线程执行 + return Executors.newCachedThreadPool(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/TokenProperties.java b/cool-admin-java/src/main/java/com/cool/core/config/TokenProperties.java new file mode 100644 index 0000000..12cd713 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/TokenProperties.java @@ -0,0 +1,18 @@ +package com.cool.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * token配置 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "cool.token") +public class TokenProperties { + // token 过期时间 + private Long expire; + // refreshToken 过期时间 + private Long refreshExpire; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/cache/CaffeineConfig.java b/cool-admin-java/src/main/java/com/cool/core/config/cache/CaffeineConfig.java new file mode 100644 index 0000000..d22204c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/cache/CaffeineConfig.java @@ -0,0 +1,122 @@ +package com.cool.core.config.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; +import java.util.Map; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.Cache; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Scheduled; + +@Slf4j +@Configuration +@EnableCaching +@ConditionalOnProperty(name = "spring.cache.type", havingValue = "CAFFEINE") +public class CaffeineConfig { + + @Value("${spring.cache.file}") + private String cacheFile; + + @Value("${cool.cacheName}") + private String cacheName; + + @Bean + public Caffeine caffeine() { + return Caffeine.newBuilder().maximumSize(10000); + } + + @Bean + public CaffeineCacheManager cacheManager(Caffeine caffeine) { + CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + cacheManager.setCaffeine(caffeine); + loadCache(cacheManager); + return cacheManager; + } + + @PostConstruct + public void init() { + File cacheDir = new File(cacheFile).getParentFile(); + if (!cacheDir.exists()) { + if (cacheDir.mkdirs()) { + log.info("Created directory: " + cacheDir.getAbsolutePath()); + } else { + log.error("Failed to create directory: " + cacheDir.getAbsolutePath()); + } + } + } + + private void loadCache(CaffeineCacheManager cacheManager) { + if (cacheManager == null) { + log.error("CacheManager is null"); + return; + } + + if (cacheFile == null || cacheFile.isEmpty()) { + log.error("Cache file path is null or empty"); + return; + } + + File file = new File(cacheFile); + if (!file.exists()) { + log.warn("Cache file does not exist: " + cacheFile); + return; + } + try (ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file))) { + Map cacheMap = (Map) inputStream.readObject(); + com.github.benmanes.caffeine.cache.Cache caffeineCache = Caffeine.newBuilder() + .build(); + caffeineCache.putAll(cacheMap); + cacheManager.registerCustomCache(cacheName, caffeineCache); + } catch (IOException | ClassNotFoundException e) { + log.error("loadCacheErr", e); + } + } + + @Bean + public CacheLoader cacheLoader(CaffeineCacheManager cacheManager) { + return new CacheLoader(cacheManager, cacheFile); + } + + class CacheLoader { + + private final CaffeineCacheManager cacheManager; + private final String cacheFile; + + public CacheLoader(CaffeineCacheManager cacheManager, String cacheFile) { + this.cacheManager = cacheManager; + this.cacheFile = cacheFile; + } + + @EventListener(ContextClosedEvent.class) + @Scheduled(fixedRate = 10000) + public void persistCache() { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null + && cache.getNativeCache() instanceof com.github.benmanes.caffeine.cache.Cache) { + Map cacheMap = ((com.github.benmanes.caffeine.cache.Cache) cache + .getNativeCache()).asMap(); + try (ObjectOutputStream outputStream = new ObjectOutputStream( + new FileOutputStream(cacheFile))) { + outputStream.writeObject(new HashMap<>(cacheMap)); + } catch (IOException e) { + log.error("persistCacheErr", e); + } + } + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/config/cache/RedisConfig.java b/cool-admin-java/src/main/java/com/cool/core/config/cache/RedisConfig.java new file mode 100644 index 0000000..9da37da --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/config/cache/RedisConfig.java @@ -0,0 +1,29 @@ +package com.cool.core.config.cache; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; + +@Configuration +@EnableCaching +@ConditionalOnProperty(name = "spring.cache.type", havingValue = "redis") +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate( + RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + + @Bean + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { + return RedisCacheManager.create(redisConnectionFactory); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/enums/AdminComponentsEnum.java b/cool-admin-java/src/main/java/com/cool/core/enums/AdminComponentsEnum.java new file mode 100644 index 0000000..e4c95f1 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/enums/AdminComponentsEnum.java @@ -0,0 +1,105 @@ +package com.cool.core.enums; + +public class AdminComponentsEnum { + + + /** + * 省市区选择器 - 用户选择省市区 + */ + public static final String PCA = "pca"; + + /** + * 文本输入 - 文本编辑框 + */ + public static final String INPUT = "input"; + + /** + * 文本域 - 多行文本编辑框 + */ + public static final String TEXTAREA = "textarea"; + + /** + * 富文本编辑器 - 用于文章,商品详情的编辑 + */ + public static final String EDITOR_RICH = "editor-rich"; + + /** + * 代码编辑器 - 用于开发人员编写代码,支持多种语言,支持代码高亮,支持代码格式化 + */ + public static final String CODING = "coding"; + + /** + * 数字输入 - 数字输入编辑框 + */ + public static final String INPUT_NUMBER = "input-number"; + + /** + * 日期选择器 - 用户选择 年-月-日 + */ + public static final String DATE = "date"; + + /** + * 日期范围选择器 - 用户选择起始 年-月-日 + */ + public static final String DATERANGE = "daterange"; + + /** + * 时间选择器 - 用户选择 时:分:秒 + */ + public static final String DATETIME = "datetime"; + + /** + * 时间范围选择器 - 用户选择起始 年-月-日 时:分:秒 + */ + public static final String DATETIMERANGE = "datetimerange"; + + /** + * 单图上传 - 用户上传单张图片,如:头像、logo、封面 + */ + public static final String UPLOAD_IMG = "upload-img"; + + /** + * 多图上传 - 用户上传多张图片, 如:照片、图片 + */ + public static final String UPLOAD_IMG_MULTIPLE = "upload-img-multiple"; + + /** + * 单个文件上传 - 用户上传单个文件 + */ + public static final String UPLOAD_FILE = "upload-file"; + + /** + * 多个文件上传 - 用户上传多个文件 + */ + public static final String UPLOAD_FILE_MULTIPLE = "upload-file-multiple"; + + /** + * 状态选择器 - 用户开启或者关闭操作,如:是否启用、是否推荐、是否默认、置顶、启用禁用等 + */ + public static final String SWITCH = "switch"; + + /** + * 评分选择器 - 用户评分 + */ + public static final String RATE = "rate"; + + /** + * 滑块选择器 - 在一个固定区间内进行选择, 如:进度 + */ + public static final String PROGRESS = "progress"; + + /** + * 单选框 - 在一组备选项中进行单选,如:审批状态 + */ + public static final String RADIO = "radio"; + + /** + * 多选框 - 适用于选项比较少的情况,在一组备选项中进行多选, 如:学历、爱好等 + */ + public static final String CHECKBOX = "checkbox"; + + /** + * 下拉框 - 适用于当选项过多时,使用下拉菜单展示并选择内容,如:分类、标签等 + */ + public static final String SELECT = "select"; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/enums/Apis.java b/cool-admin-java/src/main/java/com/cool/core/enums/Apis.java new file mode 100644 index 0000000..b8a4509 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/enums/Apis.java @@ -0,0 +1,13 @@ +package com.cool.core.enums; + +public class Apis { + public static final String ADD = "add"; + public static final String DELETE = "delete"; + public static final String UPDATE = "update"; + public static final String PAGE = "page"; + public static final String LIST = "list"; + public static final String INFO = "info"; + + + public static final String[] ALL_API = new String[]{ ADD, DELETE, UPDATE, PAGE, LIST, INFO }; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/enums/QueryModeEnum.java b/cool-admin-java/src/main/java/com/cool/core/enums/QueryModeEnum.java new file mode 100644 index 0000000..8ab1e25 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/enums/QueryModeEnum.java @@ -0,0 +1,10 @@ +package com.cool.core.enums; + +/** + * 查询模式决定返回值 + */ +public enum QueryModeEnum { + ENTITY, // 实体(默认) + ENTITY_WITH_RELATIONS, // 实体关联查询(如实体字段上加 @RelationOneToMany 等注解) + CUSTOM , // 自定义,默认为Map +} diff --git a/cool-admin-java/src/main/java/com/cool/core/enums/UserTypeEnum.java b/cool-admin-java/src/main/java/com/cool/core/enums/UserTypeEnum.java new file mode 100644 index 0000000..a35be7c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/enums/UserTypeEnum.java @@ -0,0 +1,10 @@ +package com.cool.core.enums; + +/** + * 用户类型 + */ +public enum UserTypeEnum { + ADMIN, // 后台 + APP, // app + UNKNOWN, // 未知 +} diff --git a/cool-admin-java/src/main/java/com/cool/core/eps/CoolEps.java b/cool-admin-java/src/main/java/com/cool/core/eps/CoolEps.java new file mode 100644 index 0000000..cf9e3b9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/eps/CoolEps.java @@ -0,0 +1,410 @@ +package com.cool.core.eps; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.annotation.EpsField; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.config.CustomOpenApiResource; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.*; +import java.util.stream.Collectors; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +/** + * 实体信息与路径 + */ +@Getter +@Component +@Slf4j +@RequiredArgsConstructor +public class CoolEps { + + @Value("${server.port}") + private int serverPort; + + private Dict entityInfo; + + private Dict menuInfo; + + private JSONObject swaggerInfo; + + @Value("${springdoc.api-docs.enabled:false}") + private boolean apiDocsEnabled; + + public Dict admin; + + public Dict app; + + final private RequestMappingHandlerMapping requestMappingHandlerMapping; + + @Async + public void init() { + if (!apiDocsEnabled) { + log.info("服务启动成功,端口:{}", serverPort); + return; + } + entityInfo = Dict.create(); + menuInfo = Dict.create(); + swaggerInfo = swaggerInfo(); + Runnable task = () -> { + entity(); + urls(); + log.info("初始化eps完成,服务启动成功,端口:{}", serverPort); + }; + // ThreadUtil.safeSleep(3000); + ThreadUtil.execute(task); + } + + /** + * 清空所有的数据 + */ + public void clear() { + admin = Dict.create(); + app = Dict.create(); + } + + /** + * 构建所有的url + */ + private void urls() { + Dict admin = Dict.create(); + Dict app = Dict.create(); + ArrayList emptyList = new ArrayList<>(); + Map map = requestMappingHandlerMapping.getHandlerMethods(); + for (Map.Entry methodEntry : map.entrySet()) { + RequestMappingInfo info = methodEntry.getKey(); + HandlerMethod method = methodEntry.getValue(); + TokenIgnore tokenIgnore = method.getMethodAnnotation(TokenIgnore.class); + String module = getModule(method); + if (StrUtil.isNotEmpty(module)) { + String entityName = getEntity(method.getBeanType()); + String methodPath = getMethodUrl(method); + String escapedMethodPath = methodPath.replace("{", "\\{").replace("}", "\\}"); + String prefix = Objects.requireNonNull(getUrl(info)) + .replaceFirst("(?s)(.*)" + escapedMethodPath, "$1"); + Dict result; + int type = 0; + if (prefix.startsWith("/admin")) { + result = admin; + } else if (prefix.startsWith("/app")) { + result = app; + type = 1; + } else { + continue; + } + if (result.get(module) == null) { + result.set(module, new ArrayList()); + } + + List urls = result.getBean(module); + Dict item = CollUtil.findOne(urls, dict -> { + if (dict != null) { + return dict.getStr("module").equals(module) + && dict.getStr("controller") + .equals(method.getBeanType().getSimpleName()); + } else { + return false; + } + }); + if (item != null) { + item.set("api", apis(prefix, methodPath, item.getBean("api"), tokenIgnore)); + } else { + item = Dict.create(); + item.set("controller", method.getBeanType().getSimpleName()); + item.set("module", module); + item.set("info", Dict.create().set("type", + Dict.create() + .set("name", getLastPathSegment(prefix)) + .set("description", "") + )); + item.set("api", apis(prefix, methodPath, item.getBean("api"), tokenIgnore)); + item.set("name", entityName); + item.set("columns", entityInfo.get(entityName)); + item.set("pageQueryOp", Dict.create().set("keyWordLikeFields", emptyList) + .set("fieldEq", emptyList) + .set("fieldLike", emptyList)); + item.set("prefix", prefix); + item.set("menu", menuInfo.get(entityName)); + urls.add(item); + } + if (type == 0) { + admin.set(module, urls); + } + if (type == 1) { + app.set(module, urls); + } + + } + } + this.admin = admin; + this.app = app; + + } + /** + * 提取URL路径中的最后一个路径段 + * 示例:输入 "/api/getData" 返回 "getData" + */ + private String getLastPathSegment(String url) { + if (StrUtil.isBlank(url)) { + return ""; + } + + int queryIndex = url.indexOf('?'); + if (queryIndex != -1) { + url = url.substring(0, queryIndex); + } + + int slashIndex = url.lastIndexOf('/'); + if (slashIndex != -1 && slashIndex < url.length() - 1) { + return url.substring(slashIndex + 1); + } else { + return url; + } + } + + + /** + * 设置所有的api + * + * @param prefix 路由前缀 + * @param methodPath 方法路由 + * @param list api列表 + * @return api列表 + */ + private List apis(String prefix, String methodPath, List list, TokenIgnore tokenIgnore) { + if (ObjUtil.isNull(list)) { + list = new ArrayList<>(); + } + Dict item = Dict.create(); + item.set("path", methodPath); + item.set("tag", ""); + item.set("dts", Dict.create()); + item.set("ignoreToken", false); + if (tokenIgnore != null) { + item.set("ignoreToken", true); + } + setSwaggerInfo(item, prefix + methodPath); + list.add(item); + return list; + } + + /** + * 设置swagger相关信息 + * + * @param item 信息载体 + * @param url url地址 + */ + private void setSwaggerInfo(Dict item, String url) { + JSONObject paths = swaggerInfo.getJSONObject("paths"); + JSONObject urlInfo = paths.getJSONObject(url); + String method = urlInfo.keySet().iterator().next(); + JSONObject methodInfo = urlInfo.getJSONObject(method); + item.set("method", method); + item.set("summary", methodInfo.getStr("summary")); + } + + /** + * 获得方法的url地址 + * + * @param handlerMethod 方法 + * @return 方法url地址 + */ + private String getMethodUrl(HandlerMethod handlerMethod) { + String url = ""; + Method method = handlerMethod.getMethod(); + Annotation[] annotations = method.getDeclaredAnnotations(); + + for (Annotation annotation : annotations) { + Class annotationType = annotation.annotationType(); + if (annotationType.getName().contains("org.springframework.web.bind.annotation")) { + Map attributes = Arrays.stream(annotationType.getDeclaredMethods()) + .collect(Collectors.toMap(Method::getName, m -> { + try { + return m.invoke(annotation); + } catch (Exception e) { + throw new IllegalStateException("Failed to access annotation attribute", + e); + } + })); + + if (attributes.containsKey("value") && ObjUtil.isNotEmpty(attributes.get("value"))) { + url = ((String[]) attributes.get("value"))[0]; + } + break; + } + } + + return url; + } + + /** + * 获得url地址 + * + * @param info 路由信息 + * @return url地址 + */ + private String getUrl(RequestMappingInfo info) { + if (info.getPathPatternsCondition() == null) { + return null; + } + Set paths = info.getPathPatternsCondition().getPatternValues(); + return paths.iterator().next(); + } + + /** + * 获得模块 + * + * @param method 方法 + * @return 模块 + */ + private String getModule(HandlerMethod method) { + String beanName = method.getBeanType().getName(); + String[] beanNames = beanName.split("[.]"); + int index = ArrayUtil.indexOf(beanNames, "modules"); + if (index > 0) { + return beanNames[index + 1]; + } + return null; + } + + /** + * 获得swagger的json信息 + */ + private JSONObject swaggerInfo() { + try { + byte[] bytes = SpringUtil.getBean(CustomOpenApiResource.class).getOpenApiJson(); + return JSONUtil.parseObj(new String(bytes)); + } catch (Exception e) { + return new JSONObject(); + } + } + + /** + * 获得Controller上的实体类型 + * + * @param controller Controller类 + * @return 实体名称 + */ + private String getEntity(Class controller) { + try { + ParameterizedType parameterizedType = (ParameterizedType) controller.getGenericSuperclass(); + Class entityClass = (Class) parameterizedType.getActualTypeArguments()[1]; + return entityClass.getSimpleName(); + } catch (Exception e) { + return ""; + } + } + + private void entity() { + // 扫描所有的实体类 + Set> classes = ClassUtil.scanPackageByAnnotation("", Table.class); + classes.forEach(e -> { + // 获得属性 + Field[] fields = getAllDeclaredFields(e); + List columns = columns(fields); + entityInfo.set(e.getSimpleName(), columns); + + + Table mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(e, Table.class); + + menuInfo.set(e.getSimpleName(), mergedAnnotation.comment()); + + + }); + } + + /** + * 获取类及其所有父类中声明的字段 + * + * @param clazz 要检查的类 + * @return 包含类及其所有父类中声明的所有字段的数组 + */ + public static Field[] getAllDeclaredFields(Class clazz) { + // 参数校验 + if (clazz == null) { + throw new IllegalArgumentException("Class cannot be null"); + } + + List allFields = new ArrayList<>(); + Class currentClass = clazz; + + // 循环遍历类及其父类 + while (currentClass != null) { + Field[] declaredFields = currentClass.getDeclaredFields(); + allFields.addAll(Arrays.asList(declaredFields)); + currentClass = currentClass.getSuperclass(); + } + + // 将列表转换为数组返回 + return allFields.toArray(new Field[0]); + } + + /** + * 获得所有的列 + * + * @param fields 字段名 + * @return 所有的列 + */ + private List columns(Field[] fields) { + List dictList = new ArrayList<>(); + for (Field field : fields) { + Dict dict = Dict.create(); + + EpsField epsField = AnnotatedElementUtils.findMergedAnnotation(field, EpsField.class); + if (epsField != null) { + dict.set("component", epsField.component()); + } + + ColumnDefine columnInfo = AnnotatedElementUtils.findMergedAnnotation(field, ColumnDefine.class); + if (columnInfo == null) { + continue; + } + dict.set("comment", columnInfo.comment()); + dict.set("length", columnInfo.length()); + dict.set("propertyName", field.getName()); + dict.set("type", matchType(field.getType().getName())); + dict.set("nullable", !columnInfo.notNull()); + dict.set("source", "a." + field.getName()); + dictList.add(dict); + } + return dictList; + } + + /** + * java类型转换成JavaScript对应的类型 + * + * @param type 类型 + * @return JavaScript类型 + */ + private String matchType(String type) { + return switch (type) { + case "java.lang.Boolean" -> "boolean"; + case "java.lang.Long", "java.lang.Integer", "java.lang.Short", "java.lang.Float", + "java.lang.Double" -> "number"; + case "java.util.Date" -> "date"; + default -> "string"; + }; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/eps/EpsEvent.java b/cool-admin-java/src/main/java/com/cool/core/eps/EpsEvent.java new file mode 100644 index 0000000..1e40b01 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/eps/EpsEvent.java @@ -0,0 +1,26 @@ +package com.cool.core.eps; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 事件监听 + */ +@Slf4j +@Component +@Profile({"local"}) +@RequiredArgsConstructor +public class EpsEvent { + + final private CoolEps coolEps; + + @EventListener + public void onApplicationEvent(ApplicationReadyEvent event) { + coolEps.init(); + log.info("构建eps信息"); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/exception/CoolException.java b/cool-admin-java/src/main/java/com/cool/core/exception/CoolException.java new file mode 100644 index 0000000..90498a5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/exception/CoolException.java @@ -0,0 +1,43 @@ +package com.cool.core.exception; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 自定义异常处理 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class CoolException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private String msg; + private int code = 500; + private Object data; + + public CoolException(String msg) { + super(msg); + this.msg = msg; + } + + public CoolException(String msg, Throwable e) { + super(msg, e); + this.msg = msg; + } + + public CoolException(String msg, int code) { + super(msg); + this.msg = msg; + this.code = code; + } + + public CoolException(String msg, int code, Throwable e) { + super(msg, e); + this.msg = msg; + this.code = code; + } + + public CoolException(Object data) { + this.data = data; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/exception/CoolExceptionHandler.java b/cool-admin-java/src/main/java/com/cool/core/exception/CoolExceptionHandler.java new file mode 100644 index 0000000..01dec57 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/exception/CoolExceptionHandler.java @@ -0,0 +1,71 @@ +package com.cool.core.exception; + +import cn.hutool.core.util.ObjUtil; +import com.cool.core.request.R; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 异常处理器 + */ +@RestControllerAdvice +@Slf4j +public class CoolExceptionHandler { + + @ExceptionHandler(CoolException.class) + public R handleRRException(CoolException e) { + R r = new R(); + if (ObjUtil.isNotEmpty(e.getData())) { + r.setData( e.getData() ); + } else { + r.setCode( e.getCode() ); + r.setMessage( e.getMessage() ); + } + if (ObjUtil.isNotEmpty(e.getCause())) { + log.error(e.getCause().getMessage(), e.getCause()); + } + return r; + } + + @ExceptionHandler(DuplicateKeyException.class) + public R handleDuplicateKeyException(DuplicateKeyException e) { + log.error(e.getMessage(), e); + return R.error("已存在该记录或值不能重复"); + } + + @ExceptionHandler(BadCredentialsException.class) + public R handleBadCredentialsException(BadCredentialsException e) { + log.error(e.getMessage(), e); + return R.error("账户密码不正确"); + } + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public R handleHttpRequestMethodNotSupportedException( + HttpRequestMethodNotSupportedException e) { + log.error(e.getMessage(), e); + return R.error("不支持该请求方式,请区分POST、GET等请求方式是否正确"); + } + + @ExceptionHandler(IllegalArgumentException.class) + public R handleIllegalArgumentException(IllegalArgumentException e) { + log.error(e.getMessage(), e); + return R.error(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public R handleException(Exception e) { + log.error(e.getMessage(), e); + return R.error(); + } + + @ExceptionHandler(WxErrorException.class) + public R handleException(WxErrorException e) { + log.error(e.getMessage(), e); + return R.error(e.getMessage()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/exception/CoolPreconditions.java b/cool-admin-java/src/main/java/com/cool/core/exception/CoolPreconditions.java new file mode 100644 index 0000000..8779f3a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/exception/CoolPreconditions.java @@ -0,0 +1,102 @@ +package com.cool.core.exception; + +import cn.hutool.core.util.ObjectUtil; +import com.cool.core.util.I18nUtil; +import java.util.Arrays; +import java.util.Optional; +import lombok.Getter; +import lombok.Setter; + +/** + * 校验处理 + */ +public class CoolPreconditions { + + /** + * 条件如果为真 就抛异常 如 CoolPreconditions.check(StrUtil.isEmptyIfStr(name), 500, + * "名称不能为空"); name 字段如果为 null或空字符串,就抛异常 + */ + public static void check(boolean flag, int code, String message, Object... arguments) { + if (flag) { + throw getCoolException(message, code, arguments); + } + } + + public static void check(boolean flag, String message, Object... arguments) { + if (flag) { + throw getCoolException(message, arguments); + } + } + + public static void alwaysThrow(String message, Object... arguments) { + throw getCoolException(message, arguments); + } + + private static CoolException getCoolException(String message, Object... arguments) { + Optional first = Arrays.stream(arguments).filter(o -> o instanceof Throwable) + .findFirst(); + return new CoolException(formatMessage(message, arguments), (Throwable) first.orElse(null)); + } + + private static CoolException getCoolException(String message, int code, Object... arguments) { + Optional first = Arrays.stream(arguments).filter(o -> o instanceof Throwable) + .findFirst(); + return new CoolException(formatMessage(message, arguments), code, (Throwable) first.orElse(null)); + } + + + /** + * 返回data + */ + public static void returnData(boolean flag, Object data) { + if (flag) { + throw new CoolException(data); + } + } + + public static void returnData(Object data) { + returnData(true, data); + } + + /** + * 对象如果为空 就抛异常 + */ + public static void checkEmpty(Object object, String message, Object... arguments) { + check(ObjectUtil.isEmpty(object), formatMessage(message, arguments)); + } + + public static void checkEmpty(Object object) { + check(ObjectUtil.isEmpty(object), "参数不能为空"); + } + + private static String formatMessage(String messagePattern, Object... arguments) { + messagePattern = I18nUtil.getI18nMsg(messagePattern); + StringBuilder sb = new StringBuilder(); + int argumentIndex = 0; + int placeholderIndex = messagePattern.indexOf("{}"); + while (placeholderIndex != -1) { + sb.append(messagePattern, 0, placeholderIndex); + if (argumentIndex < arguments.length) { + sb.append(arguments[argumentIndex++]); + } else { + sb.append("{}"); // 如果参数不足,保留原样 + } + messagePattern = messagePattern.substring(placeholderIndex + 2); + placeholderIndex = messagePattern.indexOf("{}"); + } + sb.append(messagePattern); // 添加剩余部分 + return sb.toString(); + } + + @Setter + @Getter + public static class ReturnData { + private Integer type; + private String message; + + public ReturnData(Integer type, String message, Object... arguments) { + this.type = type; + this.message = formatMessage(message, arguments); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/file/FileUploadStrategyFactory.java b/cool-admin-java/src/main/java/com/cool/core/file/FileUploadStrategyFactory.java new file mode 100644 index 0000000..ccbc7f4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/file/FileUploadStrategyFactory.java @@ -0,0 +1,53 @@ +package com.cool.core.file; + +import static com.cool.core.plugin.consts.PluginConsts.uploadHook; + +import cn.hutool.core.util.ObjUtil; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.file.strategy.FileUploadStrategy; +import com.cool.core.plugin.service.CoolPluginService; +import com.cool.modules.plugin.entity.PluginInfoEntity; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FileUploadStrategyFactory { + + final private ApplicationContext applicationContext; + + final private CoolPluginService coolPluginService; + + private FileUploadStrategy getStrategy(PluginInfoEntity pluginInfoEntity) { + if (ObjUtil.isEmpty(pluginInfoEntity)) { + return applicationContext.getBean("localFileUploadStrategy", FileUploadStrategy.class); + } + return applicationContext.getBean("cloudFileUploadStrategy", FileUploadStrategy.class); + } + + public Object upload(MultipartFile[] files, HttpServletRequest request) { + PluginInfoEntity pluginInfoEntity = coolPluginService.getPluginInfoEntityByHook(uploadHook); + try { + return getStrategy(pluginInfoEntity).upload(files, request, pluginInfoEntity); + } catch (IOException e) { + log.error("上传文件失败", e); + CoolPreconditions.alwaysThrow("上传文件失败 {}", e.getMessage()); + } + return null; + } + + public Object getMode() { + PluginInfoEntity pluginInfoEntity = coolPluginService.getPluginInfoEntityByHook(uploadHook); + String key = null; + if (ObjUtil.isNotEmpty(pluginInfoEntity)) { + key = pluginInfoEntity.getKey(); + } + return getStrategy(pluginInfoEntity).getMode(key); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/file/UpLoadModeType.java b/cool-admin-java/src/main/java/com/cool/core/file/UpLoadModeType.java new file mode 100644 index 0000000..eb99140 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/file/UpLoadModeType.java @@ -0,0 +1,26 @@ +package com.cool.core.file; + +import com.cool.core.config.FileModeEnum; +import lombok.Data; + +/** + * 上传模式类型 + */ +@Data +public class UpLoadModeType { + + /** + * 模式 + */ + private FileModeEnum mode; + + /** + * 类型 + */ + private String type; + + public UpLoadModeType(FileModeEnum mode) { + this.mode = mode; + this.type = mode.type(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/file/strategy/CloudFileUploadStrategy.java b/cool-admin-java/src/main/java/com/cool/core/file/strategy/CloudFileUploadStrategy.java new file mode 100644 index 0000000..4e449a0 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/file/strategy/CloudFileUploadStrategy.java @@ -0,0 +1,33 @@ +package com.cool.core.file.strategy; + +import com.cool.core.config.FileModeEnum; +import com.cool.core.util.CoolPluginInvokers; +import com.cool.modules.plugin.entity.PluginInfoEntity; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component("cloudFileUploadStrategy") +public class CloudFileUploadStrategy implements FileUploadStrategy { + + @Override + public Object upload(MultipartFile[] files, HttpServletRequest request, PluginInfoEntity pluginInfoEntity) + throws IOException { + return CoolPluginInvokers.invokePlugin(pluginInfoEntity.getKey()); + } + + @Override + public Map getMode(String key) { + try{ + Object mode = CoolPluginInvokers.invoke(key, "getMode"); + if (Objects.nonNull(mode)) { + return (Map) mode; + } + } catch (Exception ignore){} + return Map.of("mode", FileModeEnum.CLOUD.value(), + "type", FileModeEnum.CLOUD.type()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/file/strategy/FileUploadStrategy.java b/cool-admin-java/src/main/java/com/cool/core/file/strategy/FileUploadStrategy.java new file mode 100644 index 0000000..47bd605 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/file/strategy/FileUploadStrategy.java @@ -0,0 +1,38 @@ +package com.cool.core.file.strategy; + +import com.cool.modules.plugin.entity.PluginInfoEntity; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import org.springframework.web.multipart.MultipartFile; + +public interface FileUploadStrategy { + + /** + * 文件上传 + */ + Object upload(MultipartFile[] files, HttpServletRequest request, PluginInfoEntity pluginInfoEntity) + throws IOException; + + /** + * 文件上传模式 + * + * @return 上传模式 + */ + Map getMode(String key); + + default boolean isAbsolutePath(String pathStr) { + Path path = Paths.get(pathStr); + return path.isAbsolute(); + } + + default String getExtensionName(String fileName) { + if (fileName.contains(".")) { + String[] names = fileName.split("[.]"); + return "." + names[names.length - 1]; + } + return ""; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/file/strategy/LocalFileUploadStrategy.java b/cool-admin-java/src/main/java/com/cool/core/file/strategy/LocalFileUploadStrategy.java new file mode 100644 index 0000000..0be9d8e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/file/strategy/LocalFileUploadStrategy.java @@ -0,0 +1,74 @@ +package com.cool.core.file.strategy; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.cool.core.config.FileModeEnum; +import com.cool.core.config.LocalFileProperties; +import com.cool.core.exception.CoolException; +import com.cool.core.exception.CoolPreconditions; +import com.cool.modules.plugin.entity.PluginInfoEntity; +import jakarta.servlet.http.HttpServletRequest; +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component("localFileUploadStrategy") +@RequiredArgsConstructor +public class LocalFileUploadStrategy implements FileUploadStrategy { + + final private LocalFileProperties localFileProperties; + + /** + * 上传文件 + * + * @param files 上传的文件 + * @return 文件路径 + */ + @Override + public Object upload(MultipartFile[] files, HttpServletRequest request, + PluginInfoEntity pluginInfoEntity) { + CoolPreconditions.check(StrUtil.isEmpty(localFileProperties.getBaseUrl()), + "filePath 或 baseUrl 未配置"); + try { + List fileUrls = new ArrayList<>(); + String baseUrl = localFileProperties.getBaseUrl(); + String date = DateUtil.format(new Date(), + DatePattern.PURE_DATE_PATTERN); + String absoluteUploadFolder = localFileProperties.getAbsoluteUploadFolder(); + String fullPath = absoluteUploadFolder + "/" + date; + FileUtil.mkdir(fullPath); + for (MultipartFile file : files) { + // 保存文件 + String fileName = StrUtil.uuid().replaceAll("-", "") + getExtensionName( + Objects.requireNonNull(file.getOriginalFilename())); + file.transferTo(new File(fullPath + + "/" + fileName)); + fileUrls.add(baseUrl + "/" + date + "/" + fileName); + } + if (fileUrls.size() == 1) { + return fileUrls.get(0); + } + return fileUrls; + } catch (Exception e) { + throw new CoolException("文件上传失败", e); + } + } + + /** + * 文件上传模式 + * + * @return 上传模式 + */ + public Map getMode(String key) { + return Map.of("mode", FileModeEnum.LOCAL.value(), + "type", FileModeEnum.LOCAL.type()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/i18n/I18nGenerator.java b/cool-admin-java/src/main/java/com/cool/core/i18n/I18nGenerator.java new file mode 100644 index 0000000..40741a5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/i18n/I18nGenerator.java @@ -0,0 +1,219 @@ +package com.cool.core.i18n; + +import static com.cool.core.util.I18nUtil.*; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.lock.CoolLock; +import com.cool.core.util.CoolPluginInvokers; +import com.cool.core.util.I18nUtil; +import com.cool.core.util.PathUtils; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.service.sys.BaseSysMenuService; +import com.cool.modules.dict.entity.DictInfoEntity; +import com.cool.modules.dict.entity.DictTypeEntity; +import com.cool.modules.dict.service.DictInfoService; +import com.cool.modules.dict.service.DictTypeService; +import com.mybatisflex.core.query.QueryWrapper; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class I18nGenerator { + private final BaseSysMenuService baseSysMenuService; + private final DictTypeService dictTypeService; + private final DictInfoService dictInfoService; + private final CoolLock coolLock; + private final I18nUtil i18nUtil; + + private List languages; + private static final Duration DURATION = Duration.ofSeconds(30); + public void run(Map map) { + log.info("国际化 翻译..."); + languages = (List) map.getOrDefault("languages", List.of("zh-cn", "zh-tw", "en")); + path = (String) map.getOrDefault("path", "assets/i18n"); + init(); + log.info("✅国际化 翻译 成功!!!"); + enable = true; + } + + public void init() { + // 四个任务并发执行 + CompletableFuture futureMsg = CompletableFuture.runAsync(this::genBaseMsg); + CompletableFuture futureMenu = CompletableFuture.runAsync(this::genBaseMenu); + CompletableFuture futureDictInfo = CompletableFuture.runAsync(this::genBaseDictInfo); + CompletableFuture futureDictType = CompletableFuture.runAsync(this::genBaseDictType); + + // 等待全部执行完成 + CompletableFuture.allOf(futureMsg, futureMenu, futureDictInfo, futureDictType).join(); + } + + private void genBaseMsg() { + try { + Map msgMap = new HashMap<>(); + // 从idea本地启动时,从项目目录中读取 + Files.walk(Paths.get(System.getProperty("user.dir"))) + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .filter(path -> !path.toString().contains("/target/") && !path.toString().contains("/.git/")) + .forEach(path -> msgMap.putAll(processFile(path))); + if (ObjUtil.isNotEmpty(msgMap)) { + // 系统异常信息,输出到resources/i18n 文件夹下,只有本地运行会生成 + File msgfile = FileUtil.file(PathUtils.getUserDir(), + "src", "main", "resources", "cool", "i18n", "msg", "template.json"); + // 确保父目录存在 + FileUtil.mkParentDirs(msgfile); + // 写入内容 + FileUtil.writeUtf8String(JSONUtil.toJsonStr(msgMap), msgfile); + } else { + try { + // jar启动时,从jar包中读取 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource("classpath:cool/i18n/msg/template.json"); + String content = FileUtil.readUtf8String(resource.getFile()); + msgMap.putAll(JSONUtil.toBean(content, Map.class)); + } catch (Exception e) { + log.error("获取系统异常信息失败", e); + } + } + extracted(MSG_PREFIX, msgMap); + } catch (Exception e) { + log.error("国际化系统异常信息失败", e); + } + } + + /** + * 生成菜单信息国际化 + */ + @Async + public void asyncGenBaseMenu() { + if (coolLock.tryLock(MENU_PREFIX, DURATION)) { + genBaseMenu(); + coolLock.unlock(MENU_PREFIX); + } + } + private void genBaseMenu() { + try { + Map menuMap = baseSysMenuService.list(QueryWrapper.create().select(BaseSysMenuEntity::getName)) + .stream() + .collect(Collectors.toMap( + BaseSysMenuEntity::getName, + BaseSysMenuEntity::getName, + (oldValue, newValue) -> oldValue + )); + extracted(MENU_PREFIX, menuMap); + } catch (Exception e) { + log.error("国际化菜单信息失败", e); + } + } + @Async + public void asyncGenBaseDictType() { + if (coolLock.tryLock(DICT_TYPE_PREFIX, DURATION)) { + genBaseDictType(); + coolLock.unlock(DICT_TYPE_PREFIX); + } + } + private void genBaseDictType() { + try { + Map dataMap = dictTypeService.list(QueryWrapper.create().select(DictTypeEntity::getName)) + .stream() + .collect(Collectors.toMap( + DictTypeEntity::getName, + DictTypeEntity::getName, + (oldValue, newValue) -> oldValue + )); + extracted(DICT_TYPE_PREFIX, dataMap); + } catch (Exception e) { + log.error("国际化字段类型信息失败", e); + } + } + @Async + public void asyncGenBaseDictInfo() { + if (coolLock.tryLock(DICT_INFO_PREFIX, DURATION)) { + genBaseDictInfo(); + coolLock.unlock(DICT_INFO_PREFIX); + } + } + private void genBaseDictInfo() { + try { + Map dataMap = dictInfoService.list(QueryWrapper.create().select(DictInfoEntity::getName)) + .stream() + .collect(Collectors.toMap( + DictInfoEntity::getName, + DictInfoEntity::getName, + (oldValue, newValue) -> oldValue + )); + extracted(DICT_INFO_PREFIX, dataMap); + } catch (Exception e) { + log.error("国际化字段类型信息失败", e); + } + } + + private void extracted(String prefix, Map dataMap) { + languages.forEach(language -> { + String key = prefix + language; + if (!i18nUtil.exist(key)) { + JSONObject jsonObject = invokeTranslate(dataMap, language); + if (ObjUtil.isNotNull(jsonObject)) { + i18nUtil.update(key, jsonObject); + } + } + }); + } + + private JSONObject invokeTranslate(Map map, String language) { + return (JSONObject) CoolPluginInvokers.invoke("i18n", "invokeTranslate", map, language); + } + + // 匹配 CoolPreconditions 抛异常语句中的中文字符串 + private static final Pattern EXCEPTION_PATTERN = Pattern.compile( + "CoolPreconditions\\.(\\w+)\\s*\\([^;]*?\"([^\"]*[\u4e00-\u9fa5]+[^\"]*)\"", Pattern.MULTILINE + ); + + private static Map processFile(Path path) { + Map map = new HashMap<>(); + try { + String content = Files.readString(path, StandardCharsets.UTF_8); + // 去掉注释 + content = removeComments(content); + + // 仅查找方法体内的 CoolPreconditions 调用 + Matcher matcher = EXCEPTION_PATTERN.matcher(content); + while (matcher.find()) { + String chineseText = matcher.group(2).trim(); + map.put(chineseText, chineseText); + } + } catch (IOException e) { + e.printStackTrace(); + } + return map; + } + + // 移除注释(单行与多行) + private static String removeComments(String code) { + String noMultiLine = code.replaceAll("/\\*.*?\\*/", ""); // 多行注释 + return noMultiLine.replaceAll("//.*", ""); // 单行注释 + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/init/CoolPluginInit.java b/cool-admin-java/src/main/java/com/cool/core/init/CoolPluginInit.java new file mode 100644 index 0000000..846be54 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/init/CoolPluginInit.java @@ -0,0 +1,24 @@ +package com.cool.core.init; + +import com.cool.core.plugin.service.CoolPluginService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 历史安装过的插件执行初始化 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class CoolPluginInit { + + final private CoolPluginService coolPluginService; + + @EventListener(ApplicationReadyEvent.class) + public void run() { + coolPluginService.init(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/init/DBFromJsonInit.java b/cool-admin-java/src/main/java/com/cool/core/init/DBFromJsonInit.java new file mode 100644 index 0000000..9f01048 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/init/DBFromJsonInit.java @@ -0,0 +1,252 @@ +package com.cool.core.init; + + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.base.service.MapperProviderService; +import com.cool.core.mybatis.pg.PostgresSequenceSyncService; +import com.cool.core.util.DatabaseDialectUtils; +import com.cool.core.util.EntityUtils; +import com.cool.modules.base.entity.sys.BaseSysConfEntity; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.service.sys.BaseSysConfService; +import com.cool.modules.base.service.sys.BaseSysMenuService; +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.query.QueryWrapper; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.stereotype.Component; + +/** + * 数据库初始数据初始化 在 classpath:cool/data/db 目录下创建.json文件 并定义表数据, 由该类统一执行初始化 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class DBFromJsonInit { + + final private BaseSysConfService baseSysConfService; + + final private BaseSysMenuService baseSysMenuService; + + final private MapperProviderService mapperProviderService; + + final private ApplicationEventPublisher eventPublisher; + + final private PostgresSequenceSyncService postgresSequenceSyncService; + + @Value("${cool.initData}") + private boolean initData; + + @EventListener(ApplicationReadyEvent.class) + public void run() { + if (!initData) { + return; + } + // 初始化自定义的数据 + boolean initFlag = extractedDb(); + // 初始化菜单数据 + initFlag = extractedMenu() || initFlag; + // 发送数据库初始化完成事件 + eventPublisher.publishEvent(new DbInitCompleteEvent(this)); + if (initFlag) { + // 如果是postgresql,同步序列 + syncIdentitySequences(); + } + log.info("数据初始化完成!"); + } + + private void syncIdentitySequences() { + if (DatabaseDialectUtils.isPostgresql()) { + postgresSequenceSyncService.syncIdentitySequences(); + } + } + + @Getter + public static class DbInitCompleteEvent { + private final Object source; + + public DbInitCompleteEvent(Object source) { + this.source = source; + } + + } + + /** + * 解析插入业务数据 + */ + private boolean extractedDb() { + try { + // 加载 JSON 文件 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("classpath:cool/data/db/*.json"); + // 遍历所有.json文件 + return analysisResources(resources); + } catch (Exception e) { + log.error("Failed to initialize data", e); + } + return false; + } + + private boolean analysisResources(Resource[] resources) + throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { + String prefix = "db_"; + boolean isInit = false; + for (Resource resource : resources) { + File resourceFile = new File(resource.getURL().getFile()); + String fileName = prefix + resourceFile.getName(); + String value = baseSysConfService.getValue(fileName); + if (StrUtil.isNotEmpty(value)) { + log.info("{} 业务数据已初始化过...", fileName); + continue; + } + String jsonStr = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8); + JSONObject jsonObject = JSONUtil.parseObj(jsonStr); + // 遍历 JSON 文件中的数据 + analysisJson(jsonObject); + + BaseSysConfEntity baseSysUserEntity = new BaseSysConfEntity(); + baseSysUserEntity.setCKey(fileName); + baseSysUserEntity.setCValue("success"); + // 当前文件已加载 + baseSysConfService.add(baseSysUserEntity); + isInit = true; + log.info("{} 业务数据初始化成功...", fileName); + } + return isInit; + } + + private void analysisJson(JSONObject jsonObject) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Map> tableMap = EntityUtils.findTableMap(); + for (String tableName : jsonObject.keySet()) { + JSONArray records = jsonObject.getJSONArray(tableName); + // 根据表名生成实体类名和 Mapper 接口名 + Class entityClass = tableMap.get(tableName); + BaseMapper baseMapper = mapperProviderService.getMapperByEntityClass(entityClass); + // 插入 + insertList(baseMapper, entityClass, records); + } + } + + /** + * 插入列表数据 + */ + private void insertList(BaseMapper baseMapper, Class entityClass, + JSONArray records) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + // 插入数据 + for (int i = 0; i < records.size(); i++) { + JSONObject record = records.getJSONObject(i); + Object entity = JSONUtil.toBean(record, entityClass); + Method getIdMethod = entityClass.getMethod("getId"); + Object id = getIdMethod.invoke(entity); + if (ObjUtil.isNotEmpty(id) && ObjUtil.isNotEmpty( + baseMapper.selectOneById((Long) id))) { + // 数据库已经有值了 + continue; + } + if (ObjUtil.isNotEmpty(id)) { + // 带id插入 + baseMapper.insertSelectiveWithPk(entity); + } else { + baseMapper.insert(entity); + } + } + } + + /** + * 解析插入菜单数据 + */ + public boolean extractedMenu() { + boolean initFlag = false; + try { + String prefix = "menu_"; + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("classpath:cool/data/menu/*.json"); + // 遍历所有.json文件 + for (Resource resource : resources) { + File resourceFile = new File(resource.getURL().getFile()); + String fileName = prefix + resourceFile.getName(); + String value = baseSysConfService.getValue(fileName); + if (StrUtil.isNotEmpty(value)) { + log.info("{} 菜单数据已初始化过...", fileName); + continue; + } + analysisResources(resource, fileName); + initFlag = true; + } + } catch (Exception e) { + log.error("Failed to initialize data", e); + } + return initFlag; + } + + private void analysisResources(Resource resource, String fileName) throws IOException { + String jsonStr = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8); + + // 使用 解析 JSON 字符串 + JSONArray jsonArray = JSONUtil.parseArray(jsonStr); + + // 遍历 JSON 数组 + for (Object obj : jsonArray) { + JSONObject jsonObj = (JSONObject) obj; + // 将 JSON 对象转换为 Menu 对象 + parseMenu(jsonObj, null); + } + BaseSysConfEntity baseSysUserEntity = new BaseSysConfEntity(); + baseSysUserEntity.setCKey(fileName); + baseSysUserEntity.setCValue("success"); + // 当前文件已加载 + baseSysConfService.add(baseSysUserEntity); + log.info("{} 菜单数据初始化成功...", fileName); + } + + // 递归解析 JSON 对象为 Menu 对象 + private void parseMenu(JSONObject jsonObj, BaseSysMenuEntity parentMenuEntity) { + BaseSysMenuEntity menuEntity = BeanUtil.copyProperties(jsonObj, BaseSysMenuEntity.class); + if (ObjUtil.isNotEmpty(parentMenuEntity)) { + menuEntity.setParentName(parentMenuEntity.getName()); + menuEntity.setParentId(parentMenuEntity.getId()); + } + QueryWrapper queryWrapper = QueryWrapper.create() + .eq(BaseSysMenuEntity::getName, menuEntity.getName()); + if (ObjUtil.isNull(menuEntity.getParentId())) { + queryWrapper.isNull(BaseSysMenuEntity::getParentId); + } else { + queryWrapper.eq(BaseSysMenuEntity::getParentId, menuEntity.getParentId()); + } + BaseSysMenuEntity dbBaseSysMenuEntity = baseSysMenuService.getOne(queryWrapper); + if (ObjUtil.isNull(dbBaseSysMenuEntity)) { + baseSysMenuService.add(menuEntity); + } else { + menuEntity = dbBaseSysMenuEntity; + } + // 递归处理子菜单 + JSONArray childMenus = jsonObj.getJSONArray("childMenus"); + if (childMenus != null) { + for (Object obj : childMenus) { + JSONObject childObj = (JSONObject) obj; + parseMenu(childObj, menuEntity); + } + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/init/IDGenInit.java b/cool-admin-java/src/main/java/com/cool/core/init/IDGenInit.java new file mode 100644 index 0000000..446e59a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/init/IDGenInit.java @@ -0,0 +1,24 @@ +package com.cool.core.init; + +import com.cool.core.leaf.IDGenService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 唯一ID 组件初始化 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class IDGenInit { + + final private IDGenService idGenService; + + @EventListener(ApplicationReadyEvent.class) + public void run() { + idGenService.init(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/IDGenService.java b/cool-admin-java/src/main/java/com/cool/core/leaf/IDGenService.java new file mode 100644 index 0000000..b004ce6 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/IDGenService.java @@ -0,0 +1,6 @@ +package com.cool.core.leaf; + +public interface IDGenService { + long next(String key); + void init(); +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/common/CheckVO.java b/cool-admin-java/src/main/java/com/cool/core/leaf/common/CheckVO.java new file mode 100644 index 0000000..0d207a7 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/common/CheckVO.java @@ -0,0 +1,27 @@ +package com.cool.core.leaf.common; + +public class CheckVO { + private long timestamp; + private int workID; + + public CheckVO(long timestamp, int workID) { + this.timestamp = timestamp; + this.workID = workID; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public int getWorkID() { + return workID; + } + + public void setWorkID(int workID) { + this.workID = workID; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/common/Result.java b/cool-admin-java/src/main/java/com/cool/core/leaf/common/Result.java new file mode 100644 index 0000000..d85b1c6 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/common/Result.java @@ -0,0 +1,39 @@ +package com.cool.core.leaf.common; + +public class Result { + private long id; + private Status status; + + public Result() { + + } + public Result(long id, Status status) { + this.id = id; + this.status = status; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Result{"); + sb.append("id=").append(id); + sb.append(", status=").append(status); + sb.append('}'); + return sb.toString(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/common/Status.java b/cool-admin-java/src/main/java/com/cool/core/leaf/common/Status.java new file mode 100644 index 0000000..ec615e9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/common/Status.java @@ -0,0 +1,6 @@ +package com.cool.core.leaf.common; + +public enum Status { + SUCCESS, + EXCEPTION +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/package-info.java b/cool-admin-java/src/main/java/com/cool/core/leaf/package-info.java new file mode 100644 index 0000000..8278bae --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/package-info.java @@ -0,0 +1,5 @@ +/** + * 全局唯一id生成 + * 来源美团:https://github.com/Meituan-Dianping/Leaf + */ +package com.cool.core.leaf; diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/segment/SegmentIDGenImpl.java b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/SegmentIDGenImpl.java new file mode 100644 index 0000000..0c5825b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/SegmentIDGenImpl.java @@ -0,0 +1,310 @@ +package com.cool.core.leaf.segment; + +import static com.cool.core.leaf.segment.entity.table.LeafAllocEntityTableDef.LEAF_ALLOC_ENTITY; + +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.leaf.IDGenService; +import com.cool.core.leaf.common.Result; +import com.cool.core.leaf.common.Status; +import com.cool.core.leaf.segment.entity.LeafAllocEntity; +import com.cool.core.leaf.segment.mapper.LeafAllocMapper; +import com.cool.core.leaf.segment.model.Segment; +import com.cool.core.leaf.segment.model.SegmentBuffer; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import lombok.RequiredArgsConstructor; +import org.perf4j.StopWatch; +import org.perf4j.slf4j.Slf4JStopWatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class SegmentIDGenImpl implements IDGenService, DisposableBean { + + private static final Logger logger = LoggerFactory.getLogger(SegmentIDGenImpl.class); + + @Value("${leaf.segment.enable:false}") + private boolean enable; + + /** + * IDCache未初始化成功时的异常码 + */ + private static final long EXCEPTION_ID_IDCACHE_INIT_FALSE = -1; + /** + * key不存在时的异常码 + */ + private static final long EXCEPTION_ID_KEY_NOT_EXISTS = -2; + /** + * SegmentBuffer中的两个Segment均未从DB中装载时的异常码 + */ + private static final long EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL = -3; + /** + * 最大步长不超过100,0000 + */ + private static final int MAX_STEP = 1000000; + /** + * 一个Segment维持时间为15分钟 + */ + private static final long SEGMENT_DURATION = 15 * 60 * 1000L; + private final ExecutorService executorService = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new UpdateThreadFactory()); + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r); + t.setName("check-idCache-thread"); + t.setDaemon(true); + return t; + }); + private volatile boolean initOK = false; + private final Map cache = new ConcurrentHashMap<>(); + private final LeafAllocMapper leafAllocMapper; + + public static class UpdateThreadFactory implements ThreadFactory { + + private static int threadInitNumber = 0; + + private static synchronized int nextThreadNum() { + return threadInitNumber++; + } + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "Thread-Segment-Update-" + nextThreadNum()); + } + } + + @Override + public long next(String key) { + Result result = get(key); + CoolPreconditions.check(result.getId() < 0, "获取失败,code值: {}", result.getId()); + return result.getId(); + } + + @Override + public void init() { + if (enable) { + // 确保加载到kv后才初始化成功 + updateCacheFromDb(); + initOK = true; + updateCacheFromDbAtEveryMinute(); + logger.info("唯一ID组件初始化成功 ..."); + } + } + + private void updateCacheFromDbAtEveryMinute() { + scheduledExecutorService.scheduleWithFixedDelay(this::updateCacheFromDb, 60, 60, TimeUnit.SECONDS); + } + + private void updateCacheFromDb() { + logger.info("update cache from db"); + StopWatch sw = new Slf4JStopWatch(); + try { + List dbTags = leafAllocMapper.selectListByQuery(QueryWrapper.create().select( + LeafAllocEntity::getKey)).stream().map(LeafAllocEntity::getKey).toList(); + if (dbTags.isEmpty()) { + return; + } + List cacheTags = new ArrayList(cache.keySet()); + Set insertTagsSet = new HashSet<>(dbTags); + Set removeTagsSet = new HashSet<>(cacheTags); + //db中新加的tags灌进cache + for (String tmp : cacheTags) { + insertTagsSet.remove(tmp); + } + for (String tag : insertTagsSet) { + SegmentBuffer buffer = new SegmentBuffer(); + buffer.setKey(tag); + Segment segment = buffer.getCurrent(); + segment.setValue(new AtomicLong(0)); + segment.setMax(0); + segment.setStep(0); + cache.put(tag, buffer); + logger.info("Add tag {} from db to IdCache, SegmentBuffer {}", tag, buffer); + } + //cache中已失效的tags从cache删除 + for (String tmp : dbTags) { + removeTagsSet.remove(tmp); + } + for (String tag : removeTagsSet) { + cache.remove(tag); + logger.info("Remove tag {} from IdCache", tag); + } + } catch (Exception e) { + logger.warn("update cache from db exception", e); + } finally { + sw.stop("updateCacheFromDb"); + } + } + + private Result get(final String key) { + if (!initOK) { + return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION); + } + CoolPreconditions.check(!initOK, "IDCache未初始化成功"); + if (cache.containsKey(key)) { + SegmentBuffer buffer = cache.get(key); + if (!buffer.isInitOk()) { + synchronized (buffer) { + if (!buffer.isInitOk()) { + try { + updateSegmentFromDb(key, buffer.getCurrent()); + logger.info("Init buffer. Update leafkey {} {} from db", key, buffer.getCurrent()); + buffer.setInitOk(true); + } catch (Exception e) { + logger.warn("Init buffer {} exception", buffer.getCurrent(), e); + } + } + } + } + return getIdFromSegmentBuffer(cache.get(key)); + } + return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION); + } + + public void updateSegmentFromDb(String key, Segment segment) { + StopWatch sw = new Slf4JStopWatch(); + SegmentBuffer buffer = segment.getBuffer(); + LeafAllocEntity leafAllocEntity; + if (!buffer.isInitOk()) { + leafAllocEntity = updateMaxIdAndGetLeafAlloc(key); + buffer.setStep(leafAllocEntity.getStep()); + buffer.setMinStep(leafAllocEntity.getStep());//leafAlloc中的step为DB中的step + } else if (buffer.getUpdateTimestamp() == 0) { + leafAllocEntity = updateMaxIdAndGetLeafAlloc(key); + buffer.setUpdateTimestamp(System.currentTimeMillis()); + buffer.setStep(leafAllocEntity.getStep()); + buffer.setMinStep(leafAllocEntity.getStep());//leafAlloc中的step为DB中的step + } else { + long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp(); + int nextStep = buffer.getStep(); + if (duration < SEGMENT_DURATION) { + if (nextStep * 2 > MAX_STEP) { + //do nothing + } else { + nextStep = nextStep * 2; + } + } else if (duration < SEGMENT_DURATION * 2) { + //do nothing with nextStep + } else { + nextStep = nextStep / 2 >= buffer.getMinStep() ? nextStep / 2 : nextStep; + } + logger.info("leafKey[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(), String.format("%.2f",((double)duration / (1000 * 60))), nextStep); + LeafAllocEntity temp = new LeafAllocEntity(); + temp.setKey(key); + temp.setStep(nextStep); + leafAllocEntity = updateMaxIdByCustomStepAndGetLeafAlloc(temp); + buffer.setUpdateTimestamp(System.currentTimeMillis()); + buffer.setStep(nextStep); + buffer.setMinStep(leafAllocEntity.getStep());//leafAlloc的step为DB中的step + } + // must set value before set max + long value = leafAllocEntity.getMaxId() - buffer.getStep(); + segment.getValue().set(value); + segment.setMax(leafAllocEntity.getMaxId()); + segment.setStep(buffer.getStep()); + sw.stop("updateSegmentFromDb", key + " " + segment); + } + + private LeafAllocEntity updateMaxIdByCustomStepAndGetLeafAlloc(LeafAllocEntity temp) { + UpdateChain.of(LeafAllocEntity.class) + .setRaw(LeafAllocEntity::getMaxId, LEAF_ALLOC_ENTITY.MAX_ID.getName() + " + " + temp.getStep()) + .where(LeafAllocEntity::getKey).eq(temp.getKey()) + .update(); + return leafAllocMapper.selectOneByQuery(QueryWrapper.create().select( + LEAF_ALLOC_ENTITY.KEY, LEAF_ALLOC_ENTITY.MAX_ID, LEAF_ALLOC_ENTITY.STEP).eq(LeafAllocEntity::getKey, temp.getKey())); + } + + private LeafAllocEntity updateMaxIdAndGetLeafAlloc(String key) { + UpdateChain.of(LeafAllocEntity.class) + .setRaw(LeafAllocEntity::getMaxId, LEAF_ALLOC_ENTITY.MAX_ID.getName() + " + " + LEAF_ALLOC_ENTITY.STEP.getName()) + .where(LeafAllocEntity::getKey).eq(key) + .update(); + return leafAllocMapper.selectOneByQuery(QueryWrapper.create().select( + LEAF_ALLOC_ENTITY.KEY, LEAF_ALLOC_ENTITY.MAX_ID, LEAF_ALLOC_ENTITY.STEP).eq(LeafAllocEntity::getKey, key)); + } + + public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) { + while (true) { + buffer.rLock().lock(); + try { + final Segment segment = buffer.getCurrent(); + if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep()) && buffer.getThreadRunning().compareAndSet(false, true)) { + executorService.execute(() -> { + Segment next = buffer.getSegments()[buffer.nextPos()]; + boolean updateOk = false; + try { + updateSegmentFromDb(buffer.getKey(), next); + updateOk = true; + logger.info("update segment {} from db {}", buffer.getKey(), next); + } catch (Exception e) { + logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e); + } finally { + if (updateOk) { + buffer.wLock().lock(); + buffer.setNextReady(true); + buffer.getThreadRunning().set(false); + buffer.wLock().unlock(); + } else { + buffer.getThreadRunning().set(false); + } + } + }); + } + long value = segment.getValue().getAndIncrement(); + if (value < segment.getMax()) { + return new Result(value, Status.SUCCESS); + } + } finally { + buffer.rLock().unlock(); + } + waitAndSleep(buffer); + buffer.wLock().lock(); + try { + final Segment segment = buffer.getCurrent(); + long value = segment.getValue().getAndIncrement(); + if (value < segment.getMax()) { + return new Result(value, Status.SUCCESS); + } + if (buffer.isNextReady()) { + buffer.switchPos(); + buffer.setNextReady(false); + } else { + logger.error("Both two segments in {} are not ready!", buffer); + return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION); + } + } finally { + buffer.wLock().unlock(); + } + } + } + + private void waitAndSleep(SegmentBuffer buffer) { + int roll = 0; + while (buffer.getThreadRunning().get()) { + roll += 1; + if(roll > 10000) { + try { + TimeUnit.MILLISECONDS.sleep(10); + break; + } catch (InterruptedException e) { + logger.warn("Thread {} Interrupted",Thread.currentThread().getName()); + break; + } + } + } + } + + @Override + public void destroy() throws Exception { + executorService.shutdown(); + executorService.awaitTermination(10, TimeUnit.SECONDS); + + scheduledExecutorService.shutdown(); + scheduledExecutorService.awaitTermination(10, TimeUnit.SECONDS); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/segment/entity/LeafAllocEntity.java b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/entity/LeafAllocEntity.java new file mode 100644 index 0000000..a78caba --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/entity/LeafAllocEntity.java @@ -0,0 +1,27 @@ +package com.cool.core.leaf.segment.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "leaf_alloc", comment = "唯一id分配") +public class LeafAllocEntity extends BaseEntity { + + @UniIndex(name = "uk_key") + @ColumnDefine(comment = "业务key ,比如orderId", length = 20, notNull = true) + private String key; + + @ColumnDefine(comment = "当前最大id", defaultValue = "1", notNull = true) + private Long maxId; + + @ColumnDefine(comment = "步长", defaultValue = "500", notNull = true) + private Integer step; + + @ColumnDefine(comment = "描述") + private String description; +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/segment/mapper/LeafAllocMapper.java b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/mapper/LeafAllocMapper.java new file mode 100644 index 0000000..1f492b6 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/mapper/LeafAllocMapper.java @@ -0,0 +1,7 @@ +package com.cool.core.leaf.segment.mapper; + +import com.cool.core.leaf.segment.entity.LeafAllocEntity; +import com.mybatisflex.core.BaseMapper; + +public interface LeafAllocMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/Segment.java b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/Segment.java new file mode 100644 index 0000000..6908a36 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/Segment.java @@ -0,0 +1,59 @@ +package com.cool.core.leaf.segment.model; + +import java.util.concurrent.atomic.AtomicLong; + +public class Segment { + private AtomicLong value = new AtomicLong(0); + private volatile long max; + private volatile int step; + private SegmentBuffer buffer; + + public Segment(SegmentBuffer buffer) { + this.buffer = buffer; + } + + public AtomicLong getValue() { + return value; + } + + public void setValue(AtomicLong value) { + this.value = value; + } + + public long getMax() { + return max; + } + + public void setMax(long max) { + this.max = max; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public SegmentBuffer getBuffer() { + return buffer; + } + + public long getIdle() { + return this.getMax() - getValue().get(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Segment("); + sb.append("value:"); + sb.append(value); + sb.append(",max:"); + sb.append(max); + sb.append(",step:"); + sb.append(step); + sb.append(")"); + return sb.toString(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/SegmentBuffer.java b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/SegmentBuffer.java new file mode 100644 index 0000000..44c33da --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/leaf/segment/model/SegmentBuffer.java @@ -0,0 +1,129 @@ +package com.cool.core.leaf.segment.model; + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 双buffer + */ +public class SegmentBuffer { + private String key; + private Segment[] segments; //双buffer + private volatile int currentPos; //当前的使用的segment的index + private volatile boolean nextReady; //下一个segment是否处于可切换状态 + private volatile boolean initOk; //是否初始化完成 + private final AtomicBoolean threadRunning; //线程是否在运行中 + private final ReadWriteLock lock; + + private volatile int step; + private volatile int minStep; + private volatile long updateTimestamp; + + public SegmentBuffer() { + segments = new Segment[]{new Segment(this), new Segment(this)}; + currentPos = 0; + nextReady = false; + initOk = false; + threadRunning = new AtomicBoolean(false); + lock = new ReentrantReadWriteLock(); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Segment[] getSegments() { + return segments; + } + + public Segment getCurrent() { + return segments[currentPos]; + } + + public int getCurrentPos() { + return currentPos; + } + + public int nextPos() { + return (currentPos + 1) % 2; + } + + public void switchPos() { + currentPos = nextPos(); + } + + public boolean isInitOk() { + return initOk; + } + + public void setInitOk(boolean initOk) { + this.initOk = initOk; + } + + public boolean isNextReady() { + return nextReady; + } + + public void setNextReady(boolean nextReady) { + this.nextReady = nextReady; + } + + public AtomicBoolean getThreadRunning() { + return threadRunning; + } + + public Lock rLock() { + return lock.readLock(); + } + + public Lock wLock() { + return lock.writeLock(); + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public int getMinStep() { + return minStep; + } + + public void setMinStep(int minStep) { + this.minStep = minStep; + } + + public long getUpdateTimestamp() { + return updateTimestamp; + } + + public void setUpdateTimestamp(long updateTimestamp) { + this.updateTimestamp = updateTimestamp; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("SegmentBuffer{"); + sb.append("key='").append(key).append('\''); + sb.append(", segments=").append(Arrays.toString(segments)); + sb.append(", currentPos=").append(currentPos); + sb.append(", nextReady=").append(nextReady); + sb.append(", initOk=").append(initOk); + sb.append(", threadRunning=").append(threadRunning); + sb.append(", step=").append(step); + sb.append(", minStep=").append(minStep); + sb.append(", updateTimestamp=").append(updateTimestamp); + sb.append('}'); + return sb.toString(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/lock/CoolLock.java b/cool-admin-java/src/main/java/com/cool/core/lock/CoolLock.java new file mode 100644 index 0000000..8ff0a80 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/lock/CoolLock.java @@ -0,0 +1,108 @@ +package com.cool.core.lock; + + +import jakarta.annotation.PostConstruct; +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.cache.CacheType; +import org.springframework.cache.CacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class CoolLock { + // 缓存类型 + @Value("${spring.cache.type}") + private String type; + + @Value("${cool.cacheName}") + private String cacheName; + + private final CacheManager cacheManager; + private RedisCacheWriter redisCache ; + + // 非redis方式时使用 + private static final Map lockMap = new ConcurrentHashMap<>(); + + private static final String LOCK_PREFIX = "lock:"; + + @PostConstruct + private void init() { + this.type = type.toLowerCase(); + if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + redisCache = (RedisCacheWriter) Objects.requireNonNull(cacheManager.getCache(cacheName)) + .getNativeCache(); + } + } + /** + * 尝试获取锁 + * + * @param key 锁的 key + * @param expireTime 锁的过期时间 + * @return 如果成功获取锁则返回 true,否则返回 false + */ + public boolean tryLock(String key, Duration expireTime) { + String lockKey = getLockKey(key); + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Lock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock()); + return lock.tryLock(); + } + byte[] lockKeyBytes = lockKey.getBytes(); + // 使用 putIfAbsent 来尝试设置锁,如果成功返回 true,否则返回 false + return redisCache.putIfAbsent(cacheName, lockKeyBytes, new byte[0], expireTime) == null; + } + + /** + * 释放锁 + */ + public void unlock(String key) { + String lockKey = getLockKey(key); + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Lock lock = lockMap.get(lockKey); + if (lock != null && lock.tryLock()) { + lock.unlock(); + lockMap.remove(lockKey); + } + return; + } + redisCache.remove(cacheName, lockKey.getBytes()); + } + + /** + * 拼接锁前缀 + */ + private String getLockKey(String key) { + return LOCK_PREFIX + key; + } + + /** + * 等待锁 + * + * @param key 锁的 key + * @param expireTime 锁的过期时间 + * @return 如果成功获取锁则返回 true,否则返回 false + */ + public boolean waitForLock(String key, Duration expireTime, Duration waitTime) { + long endTime = System.currentTimeMillis() + waitTime.toMillis(); + while (System.currentTimeMillis() < endTime) { + if (tryLock(key, expireTime)) { + return true; + } + // 等待锁释放 + try { + Thread.sleep(100); // 可以根据需要调整等待时间 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/BaseJsonTypeHandler.java b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/BaseJsonTypeHandler.java new file mode 100644 index 0000000..a403c46 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/BaseJsonTypeHandler.java @@ -0,0 +1,48 @@ +package com.cool.core.mybatis.handler; + +import com.cool.core.util.DatabaseDialectUtils; +import com.mybatisflex.core.util.StringUtil; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.postgresql.util.PGobject; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +public abstract class BaseJsonTypeHandler extends BaseTypeHandler { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { + if (DatabaseDialectUtils.isPostgresql()) { + PGobject jsonObject = new PGobject(); + jsonObject.setType("json"); + jsonObject.setValue(toJson(parameter)); + ps.setObject(i, jsonObject); + } else { + ps.setString(i, toJson(parameter)); + } + } + + @Override + public T getNullableResult(ResultSet rs, String columnName) throws SQLException { + final String json = rs.getString(columnName); + return StringUtil.noText(json) ? null : parseJson(json); + } + + @Override + public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + final String json = rs.getString(columnIndex); + return StringUtil.noText(json) ? null : parseJson(json); + } + + @Override + public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + final String json = cs.getString(columnIndex); + return StringUtil.noText(json) ? null : parseJson(json); + } + + protected abstract T parseJson(String json); + + protected abstract String toJson(T object); + +} diff --git a/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/Fastjson2TypeHandler.java b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/Fastjson2TypeHandler.java new file mode 100644 index 0000000..aad475e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/Fastjson2TypeHandler.java @@ -0,0 +1,73 @@ +package com.cool.core.mybatis.handler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.TypeReference; + +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; + +public class Fastjson2TypeHandler extends BaseJsonTypeHandler { + + private final Class propertyType; + private Class genericType; + private Type type; + + private boolean supportAutoType = false; + + public Fastjson2TypeHandler(Class propertyType) { + this.propertyType = propertyType; + this.supportAutoType = propertyType.isInterface() || Modifier.isAbstract(propertyType.getModifiers()); + } + + + public Fastjson2TypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; + this.type = TypeReference.collectionType((Class) propertyType, genericType); + + Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (actualTypeArgument instanceof Class) { + this.supportAutoType = ((Class) actualTypeArgument).isInterface() + || Modifier.isAbstract(((Class) actualTypeArgument).getModifiers()); + } + } + + @Override + protected Object parseJson(String json) { + if (genericType != null && Collection.class.isAssignableFrom(propertyType)) { + if (supportAutoType) { + return JSON.parseArray(json, Object.class, JSONReader.Feature.SupportAutoType); + } else { + return JSON.parseObject(json, type); + } + + } else { + if (supportAutoType) { + return JSON.parseObject(json, Object.class, JSONReader.Feature.SupportAutoType); + } else { + return JSON.parseObject(json, propertyType); + } + } + } + + @Override + protected String toJson(Object object) { + if (supportAutoType) { + return JSON.toJSONString(object + , JSONWriter.Feature.WriteMapNullValue + , JSONWriter.Feature.WriteNullListAsEmpty + , JSONWriter.Feature.WriteNullStringAsEmpty, JSONWriter.Feature.WriteClassName + ); + } else { + return JSON.toJSONString(object + , JSONWriter.Feature.WriteMapNullValue + , JSONWriter.Feature.WriteNullListAsEmpty + , JSONWriter.Feature.WriteNullStringAsEmpty + ); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/JacksonTypeHandler.java b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/JacksonTypeHandler.java new file mode 100644 index 0000000..e92d096 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/mybatis/handler/JacksonTypeHandler.java @@ -0,0 +1,68 @@ +package com.cool.core.mybatis.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mybatisflex.core.exception.FlexExceptions; + +import java.io.IOException; +import java.util.Collection; + +public class JacksonTypeHandler extends BaseJsonTypeHandler { + + private static ObjectMapper objectMapper; + private final Class propertyType; + private Class genericType; + private JavaType javaType; + + public JacksonTypeHandler(Class propertyType) { + this.propertyType = propertyType; + } + + public JacksonTypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; + } + + @Override + protected Object parseJson(String json) { + try { + if (genericType != null && Collection.class.isAssignableFrom(propertyType)) { + return getObjectMapper().readValue(json, getJavaType()); + } else { + return getObjectMapper().readValue(json, propertyType); + } + } catch (IOException e) { + throw FlexExceptions.wrap(e, "Can not parseJson by JacksonTypeHandler: " + json); + } + } + + @Override + protected String toJson(Object object) { + try { + return getObjectMapper().writeValueAsString(object); + } catch (JsonProcessingException e) { + throw FlexExceptions.wrap(e, "Can not convert object to Json by JacksonTypeHandler: " + object); + } + } + + + public JavaType getJavaType() { + if (javaType == null){ + javaType = getObjectMapper().getTypeFactory().constructCollectionType((Class) propertyType, genericType); + } + return javaType; + } + + public static ObjectMapper getObjectMapper() { + if (null == objectMapper) { + objectMapper = new ObjectMapper(); + } + return objectMapper; + } + + public static void setObjectMapper(ObjectMapper objectMapper) { + JacksonTypeHandler.objectMapper = objectMapper; + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/mybatis/pg/PostgresSequenceSyncService.java b/cool-admin-java/src/main/java/com/cool/core/mybatis/pg/PostgresSequenceSyncService.java new file mode 100644 index 0000000..d54b34c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/mybatis/pg/PostgresSequenceSyncService.java @@ -0,0 +1,66 @@ +package com.cool.core.mybatis.pg; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.List; +import java.util.Map; +/** + * PostgreSQL Identity 序列同步服务 + * 解决PostgreSQL 默认的序列机制,序列会自动递增,当手动插入指定id时需调用同步接口,否则id会重复。 + */ +@Slf4j +@Service +public class PostgresSequenceSyncService { + + private final JdbcTemplate jdbcTemplate; + + public PostgresSequenceSyncService(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void syncIdentitySequences() { + log.info("⏳ 开始同步 PostgreSQL Identity 序列..."); + + // 查询所有 identity 字段 + String identityColumnQuery = """ + SELECT table_schema, table_name, column_name + FROM information_schema.columns + WHERE is_identity = 'YES' + AND table_schema = 'public' + """; + + List> identityColumns = jdbcTemplate.queryForList(identityColumnQuery); + + for (Map col : identityColumns) { + String schema = (String) col.get("table_schema"); + String table = (String) col.get("table_name"); + String column = (String) col.get("column_name"); + + String fullTable = schema + "." + table; + + // 获取对应的序列名 + String seqNameSql = "SELECT pg_get_serial_sequence(?, ?)"; + String seqName = jdbcTemplate.queryForObject(seqNameSql, String.class, fullTable, column); + + if (seqName == null) { + log.warn("⚠️ 无法获取序列:{}.{}", table, column); + continue; + } + + // 获取当前最大 ID + Long maxId = jdbcTemplate.queryForObject( + String.format("SELECT COALESCE(MAX(%s), 0) FROM %s", column, fullTable), + Long.class + ); + + if (maxId != null && maxId > 0) { // 正确的:setval 有返回值,必须用 queryForObject + String setvalSql = "SELECT setval(?, ?)"; + Long newVal = jdbcTemplate.queryForObject(setvalSql, Long.class, seqName, maxId); + log.info("✅ 同步序列 [{}] -> 当前最大 ID: {}", seqName, newVal); + } + } + + log.info("✅ PostgreSQL Identity 序列同步完成。"); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/request/CrudOption.java b/cool-admin-java/src/main/java/com/cool/core/request/CrudOption.java new file mode 100644 index 0000000..c16954b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/CrudOption.java @@ -0,0 +1,202 @@ +package com.cool.core.request; + + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.cool.core.enums.QueryModeEnum; +import com.cool.core.util.ConvertUtil; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryCondition; +import com.mybatisflex.core.query.QueryTable; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import lombok.Data; +import org.springframework.core.env.Environment; + +/** + * 查询构建器 + * + * @param + */ +@Data +public class CrudOption { + + private QueryWrapper queryWrapper; + private QueryColumn[] fieldEq; + private QueryColumn[] keyWordLikeFields; + private QueryColumn[] select; + private JSONObject requestParams; + + private QueryModeEnum queryModeEnum; + + private Transform transform; + + public interface Transform { + void apply(B obj); + } + + /** + * queryModeEnum 为 CUSTOM,可设置 默认为Map + */ + private Class asType; + + private Environment evn; + + public CrudOption(JSONObject requestParams) { + this.requestParams = requestParams; + this.queryWrapper = QueryWrapper.create(); + this.evn = SpringUtil.getBean(Environment.class); + queryModeEnum = QueryModeEnum.ENTITY; + } + + public QueryWrapper getQueryWrapper(Class entityClass) { + return build(this.queryWrapper, entityClass); + } + + public CrudOption queryWrapper(QueryWrapper queryWrapper) { + this.queryWrapper = queryWrapper; + return this; + } + + /** + * 按前端传上来的字段值做eq + */ + public CrudOption fieldEq(QueryColumn... fields) { + this.fieldEq = fields; + return this; + } + + /** + * 按前端传上来的字段值做like + */ + public CrudOption keyWordLikeFields(QueryColumn... fields) { + this.keyWordLikeFields = fields; + return this; + } + + /** + * 需要返回给前端的字段 + */ + public CrudOption select(QueryColumn... selects) { + this.select = selects; + return this; + } + + /** + * 查询模式决定返回值 + * 目前有三种模式,按实体查询返回、关联查询返回(实体字段上加 @RelationOneToMany 等注解)、自定义返回结果 + */ + public CrudOption queryModeEnum(QueryModeEnum queryModeEnum) { + this.queryModeEnum = queryModeEnum; + if (ObjUtil.equal(queryModeEnum, QueryModeEnum.CUSTOM) + && ObjUtil.isEmpty(asType)) { + asType = Map.class; + } + return this; + } + + /** + * 自定义返回结果对象类型 + */ + public CrudOption asType(Class asType) { + this.asType = asType; + return this; + } + + /** + * 转换参数,组装数据 + */ + public CrudOption transform(Transform transform) { + this.transform = transform; + return this; + } + + /** + * 构建查询条件 + * + * @return QueryWrapper + */ + private QueryWrapper build(QueryWrapper queryWrapper, Class entityClass) { + if (ObjectUtil.isNotEmpty(fieldEq)) { + Arrays.stream(fieldEq).toList().forEach(filed -> { + String filedName = StrUtil.toCamelCase(filed.getName()); + Object obj = requestParams.get(filedName); + if (ObjUtil.isEmpty(obj)) { + return; + } + if (obj instanceof JSONArray) { + // 集合 + queryWrapper.and(filed.in(ConvertUtil.covertListByClass(filedName, (JSONArray)obj, entityClass).toArray())); + } else { + // 对象 + queryWrapper.and(filed.eq(ConvertUtil.convertByClass(filedName, obj, entityClass))); + } + }); + } + if (ObjectUtil.isNotEmpty(this.keyWordLikeFields)) { + Object keyWord = requestParams.get("keyWord"); + if (ObjectUtil.isEmpty(keyWord)) { + // // keyWord值为空,遍历keyWordLikeFields字段,根据queryColumn字段名构建查询条件 + for (QueryColumn queryColumn : keyWordLikeFields) { + String fieldName = queryColumn.getName(); + String paramName = StrUtil.toCamelCase(fieldName); + String paramValue = requestParams.getStr(paramName); + if (ObjectUtil.isNotEmpty(paramValue)) { + queryWrapper.and(queryColumn.like(paramValue)); + } + } + } else { + // keyWord值非空,使用keyWord构建 + // 初始化一个空的 QueryCondition + QueryCondition orCondition = null; + for (QueryColumn queryColumn : keyWordLikeFields) { + QueryCondition condition = queryColumn.like(keyWord); + if (orCondition == null) { + orCondition = condition; + } else { + orCondition = orCondition.or(condition); + } + } + queryWrapper.and(orCondition); + } + } + if (ObjectUtil.isNotEmpty(select)) { + queryWrapper.select(select); + } + // 排序 + order(queryWrapper, entityClass); + return queryWrapper; + } + + private void order(QueryWrapper queryWrapper, Class entityClass) { + Table tableAnnotation = AnnotationUtil.getAnnotation(entityClass, Table.class); + if (ObjectUtil.isEmpty(tableAnnotation)) { + // 该对象没有@Table注解,非Entity对象 + return; + } + String tableAlias = ""; + List queryTables = (List) ReflectUtil.getFieldValue(queryWrapper, "queryTables"); + if (ObjectUtil.isNotEmpty(queryTables)) { + // 取主表作为排序字段别名 + QueryTable queryTable = queryTables.get(0); + tableAlias = queryTable.getName() + "."; + } + String order = requestParams.getStr("order", + tableAnnotation.camelToUnderline() ? "create_time" : "createTime"); + String sort = requestParams.getStr("sort", "desc"); + if (StrUtil.isNotEmpty(order) && StrUtil.isNotEmpty(sort)) { + queryWrapper.orderBy( + tableAlias + (tableAnnotation.camelToUnderline() ? StrUtil.toUnderlineCase(order) : order), + sort.equals("asc")); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/request/PageResult.java b/cool-admin-java/src/main/java/com/cool/core/request/PageResult.java new file mode 100644 index 0000000..314a96f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/PageResult.java @@ -0,0 +1,33 @@ +package com.cool.core.request; + +import java.util.List; +import com.mybatisflex.core.paginate.Page; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema( title = "分页数据模型" ) +public class PageResult { + @Schema( title = "分页数据" ) + private List list; + private Pagination pagination = new Pagination(); + + @Data + public static class Pagination { + @Schema( title = "页码" ) + private Long page; + @Schema( title = "本页数量" ) + private Long size; + @Schema( title = "总页数" ) + private Long total; + } + + static public PageResult of(Page page ){ + PageResult result = new PageResult(); + result.setList(page.getRecords()); + result.pagination.setPage( page.getPageNumber() ); + result.pagination.setSize( page.getPageSize() ); + result.pagination.setTotal( page.getTotalRow() ); + return result; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/request/R.java b/cool-admin-java/src/main/java/com/cool/core/request/R.java new file mode 100644 index 0000000..c152ff9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/R.java @@ -0,0 +1,74 @@ +package com.cool.core.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 返回信息 + */ +@Schema(title = "响应数据结构") +@Data +public class R implements Serializable { + private static final long serialVersionUID = 1L; + + + @Schema(title = "编码:1000表示成功,其他值表示失败") + private int code = 1000; + + @Schema(title = "消息内容") + private String message = "success"; + + @Schema(title = "响应数据") + private T data; + + public R() { + + } + + public R( int code, String message, T data ) { + this.code = code; + this.message = message; + this.data = data; + } + + public static R error() { + return error(1001, "请求方式不正确或服务出现异常"); + } + + public static R error(String msg) { + return error(1001, msg); + } + + public static R error(int code, String msg) { + R r = new R(); + r.code = code; + r.message = msg; + return r; + } + + public static R okMsg(String msg) { + R r = new R(); + r.message = msg; + return r; + } + + public static R ok() { + return new R(); + } + + public static R ok(B data) { + return new R(1000 , "success", data); + } + + + public R put(String key, Object value) { + switch (key) { + case "code" -> this.code = (int) value; + case "message" -> this.message = (String) value; + case "data" -> this.data = (T) value; + } + return this; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/request/RequestParamsFilter.java b/cool-admin-java/src/main/java/com/cool/core/request/RequestParamsFilter.java new file mode 100644 index 0000000..cbcc551 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/RequestParamsFilter.java @@ -0,0 +1,114 @@ +package com.cool.core.request; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.jwt.JWT; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.util.BodyReaderHttpServletRequestWrapper; +import com.cool.core.util.CoolSecurityUtil; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +/** + * 封装请求参数 URL参数 和 body JSON 到同一个 JSONObject 方便读取 + */ +@Component +@Order(2) +public class RequestParamsFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + Filter.super.init(filterConfig); + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + // 防止流读取一次后就没有了, 所以需要将流继续写出去 + HttpServletRequest request = (HttpServletRequest) servletRequest; + JSONObject requestParams = new JSONObject(); + String language = request.getHeader("language"); + String coolEid = request.getHeader("cool-admin-eid"); + Long tenantId = StrUtil.isEmpty(coolEid) ? null : Long.parseLong(coolEid); + if (StrUtil.isNotEmpty(request.getContentType()) && request.getContentType().contains("multipart/form-data")) { + servletRequest.setAttribute("requestParams", requestParams); + servletRequest.setAttribute("cool-language", language); + servletRequest.setAttribute("tenantId", tenantId); + filterChain.doFilter(servletRequest, servletResponse); + } else { + BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request); + String body = requestWrapper.getBodyString(requestWrapper); + if (StrUtil.isNotEmpty(body) && JSONUtil.isTypeJSON(body) && !JSONUtil.isTypeJSONArray( + body)) { + requestParams = JSONUtil.parseObj(body); + } + Object jwtObj = request.getAttribute("tokenInfo"); + if (jwtObj != null) { + requestParams.set("tokenInfo", ((JWT) jwtObj).getPayload().getClaimsJson()); + } + // 登录状态,设置用户id + Long currTenantId = setUserId(requestParams); + if (ObjUtil.isNotNull(currTenantId)) { + tenantId = currTenantId; + } + requestWrapper.setAttribute("cool-language", language); + request.setAttribute("tenantId", tenantId); + requestParams.set("body", body); + requestParams.putAll(getAllRequestParam(request)); + + requestWrapper.setAttribute("requestParams", requestParams); + filterChain.doFilter(requestWrapper, servletResponse); + } + } + + private Long setUserId(JSONObject requestParams) { + UserTypeEnum userTypeEnum = CoolSecurityUtil.getCurrentUserType(); + switch (userTypeEnum) { + // 只有登录了,才有用户类型, 不然为 UNKNOWN 状态 + case ADMIN -> { + // 管理后台由于之前已经有逻辑再了,怕会影响到,如果自己有传了值不覆盖 + Object o = requestParams.get("userId"); + if (ObjUtil.isNull(o)) { + requestParams.set("userId", CoolSecurityUtil.getCurrentUserId()); + } + } + // app端,userId 为当前登录的用户id + case APP -> requestParams.set("userId", CoolSecurityUtil.getCurrentUserId()); + } + return CoolSecurityUtil.getTenantId(requestParams); + } + + /** + * 获取客户端请求参数中所有的信息 + * + */ + private Map getAllRequestParam(final HttpServletRequest request) { + Map res = new HashMap<>(); + Enumeration temp = request.getParameterNames(); + if (null != temp) { + while (temp.hasMoreElements()) { + String en = (String) temp.nextElement(); + String value = request.getParameter(en); + res.put(en, value); + // 如果字段的值为空,判断若值为空,则删除这个字段> + if (null == res.get(en) || "".equals(res.get(en))) { + res.remove(en); + } + } + } + return res; + } + + @Override + public void destroy() { + Filter.super.destroy(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/request/RestInterceptor.java b/cool-admin-java/src/main/java/com/cool/core/request/RestInterceptor.java new file mode 100644 index 0000000..21a85cb --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/RestInterceptor.java @@ -0,0 +1,39 @@ +package com.cool.core.request; + +import com.cool.core.annotation.CoolRestController; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Arrays; + +/** + * 通用方法rest接口 + */ +@Component +public class RestInterceptor implements HandlerInterceptor { + private final static String[] rests = { "add", "delete", "update", "info", "list", "page" }; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 判断有无通用方法 + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + CoolRestController coolRestController = handlerMethod.getBeanType().getAnnotation(CoolRestController.class); + if (null != coolRestController) { + String[] urls = request.getRequestURI().split("/"); + String rest = urls[urls.length - 1]; + if (Arrays.asList(rests).contains(rest)) { + if (!Arrays.asList(coolRestController.api()).contains(rest)) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return false; + } + } + } + } + return true; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixConfiguration.java b/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixConfiguration.java new file mode 100644 index 0000000..c699a80 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixConfiguration.java @@ -0,0 +1,17 @@ +package com.cool.core.request.prefix; + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +/** + * 自定义路由规则 + */ +@Component +public class AutoPrefixConfiguration implements WebMvcRegistrations { + + @Override + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + return new AutoPrefixUrlMapping(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixUrlMapping.java b/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixUrlMapping.java new file mode 100644 index 0000000..01f04ea --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/request/prefix/AutoPrefixUrlMapping.java @@ -0,0 +1,103 @@ +package com.cool.core.request.prefix; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.enums.Apis; +import com.cool.core.util.ConvertUtil; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +/** + * 自动配置模块的路由 + */ +@Slf4j +public class AutoPrefixUrlMapping extends RequestMappingHandlerMapping { + + @Override + protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { + CoolRestController[] annotations = handlerType.getAnnotationsByType(CoolRestController.class); + RequestMappingInfo info = super.getMappingForMethod(method, handlerType); + String packageName = handlerType.getPackage().getName(); + if (info != null && annotations.length > 0 && annotations[0].value().length == 0 + && packageName.contains("modules")) { + if (!checkApis(annotations, info)) { + return null; + } + String prefix = getPrefix(packageName); + String cName = getCName(annotations[0].cname(), handlerType, prefix); + info = info.mutate().paths(prefix + "/" + cName).build().combine(info); + } + return info; + } + + /** + * 根据配置检查是否构建路由 + * + * @param annotations 注解 + * @param info 路由信息 + * @return 是否需要构建路由 + */ + private boolean checkApis(CoolRestController[] annotations, RequestMappingInfo info) { + String[] apis = Apis.ALL_API; + if (info.getPathPatternsCondition() == null) { + return true; + } + List setApis; + if (ArrayUtil.isNotEmpty(annotations)) { + CoolRestController coolRestController = annotations[0]; + setApis = CollUtil.toList(coolRestController.api()); + + Set methodPaths = info.getPathPatternsCondition().getPatternValues(); + String methodPath = methodPaths.iterator().next().replace("/", ""); + if (!CollUtil.toList(apis).contains(methodPath)) { + return true; + } else { + return setApis.contains(methodPath); + } + } + return false; + } + + /** + * 根据Controller名称构建路由地址 + * + * @param handlerType 类 + * @param prefix 路由前缀 + * @return url地址 + */ + private String getCName(String cname, Class handlerType, String prefix) { + if (ObjUtil.isNotEmpty(cname)) { + return cname; + } + String name = handlerType.getName(); + String[] names = name.split("[.]"); + name = names[names.length - 1]; + return ConvertUtil.extractController2Path(ConvertUtil.pathToClassName(prefix), name); + } + + /** + * 构建路由前缀 + * + * @param packageName 包名 + * @return 返回路由前缀 + */ + private String getPrefix(String packageName) { + String dotPath = packageName.split("modules")[1]; // 将包路径中多于的部分截取掉 + String[] dotPaths = dotPath.replace(".controller", "").split("[.]"); + List paths = CollUtil.toList(dotPaths); + paths.removeIf(String::isEmpty); + // 第一和第二位互换位置 + String p0 = paths.get(0); + String p1 = paths.get(1); + paths.set(0, p1); + paths.set(1, p0); + dotPath = "/" + CollUtil.join(paths, "/"); + return dotPath; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/security/EntryPointUnauthorizedHandler.java b/cool-admin-java/src/main/java/com/cool/core/security/EntryPointUnauthorizedHandler.java new file mode 100644 index 0000000..1d86f8a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/EntryPointUnauthorizedHandler.java @@ -0,0 +1,34 @@ +package com.cool.core.security; + +import cn.hutool.json.JSONUtil; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; + +/** + * 自定401返回值 + */ +@Component +public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException { + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + response.getWriter().write(JSONUtil.toJsonStr(new HashMap() { + { + put("code", "401"); + put("message", "未登录"); + } + })); + response.setStatus(401); + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/security/IgnoredUrlsProperties.java b/cool-admin-java/src/main/java/com/cool/core/security/IgnoredUrlsProperties.java new file mode 100644 index 0000000..2443dae --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/IgnoredUrlsProperties.java @@ -0,0 +1,22 @@ +package com.cool.core.security; + +import java.util.ArrayList; +import java.util.List; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 忽略地址配置 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "ignored") +public class IgnoredUrlsProperties { + + // 忽略后台校验权限列表 + private List adminAuthUrls = new ArrayList<>(); + + // 忽略记录请求日志列表 + private List logUrls = new ArrayList<>(); +} diff --git a/cool-admin-java/src/main/java/com/cool/core/security/JwtAuthenticationTokenFilter.java b/cool-admin-java/src/main/java/com/cool/core/security/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..05209df --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/JwtAuthenticationTokenFilter.java @@ -0,0 +1,110 @@ +package com.cool.core.security; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.jwt.JWT; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.security.jwt.JwtTokenUtil; +import com.cool.core.security.jwt.JwtUser; +import com.cool.core.util.PathUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +/** + * Token过滤器 + */ +@Order(1) +@Component +@RequiredArgsConstructor +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + final private JwtTokenUtil jwtTokenUtil; + final private CoolCache coolCache; + final private IgnoredUrlsProperties ignoredUrlsProperties; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain chain) + throws ServletException, IOException { + String requestURI = request.getRequestURI(); + if (PathUtils.isMatch(ignoredUrlsProperties.getAdminAuthUrls(), requestURI)) { + // 请求路径在忽略后台鉴权url里支持通配符,放行 + chain.doFilter(request, response); + return; + } + String authToken = request.getHeader("Authorization"); + if (!StrUtil.isEmpty(authToken)) { + JWT jwt = jwtTokenUtil.getTokenInfo(authToken); + + Object userType = jwt.getPayload("userType"); + if (Objects.equals(userType, UserTypeEnum.APP.name())) { + // app + handlerAppRequest(request, jwt, authToken); + } else { + // admin + handlerAdminRequest(request, jwt, authToken); + } + } + chain.doFilter(request, response); + } + /** + * 处理app请求 + */ + private void handlerAppRequest(HttpServletRequest request, JWT jwt, String authToken) { + String userId = jwt.getPayload("userId").toString(); + if (ObjectUtil.isNotEmpty(userId) + && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = coolCache.get("app:userDetails:" + userId, + JwtUser.class); + if (jwtTokenUtil.validateToken(authToken) && userDetails != null) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + authentication.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("userId", jwt.getPayload("userId")); + request.setAttribute("tokenInfo", jwt); + } + } + } + + /** + * 处理后台请求 + */ + private void handlerAdminRequest(HttpServletRequest request, JWT jwt, String authToken) { + String username = jwt.getPayload("username").toString(); + if (username != null + && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = coolCache.get("admin:userDetails:" + username, + JwtUser.class); + Integer passwordV = Convert.toInt(jwt.getPayload("passwordVersion")); + Integer rv = coolCache.get("admin:passwordVersion:" + jwt.getPayload("userId"), + Integer.class); + if (jwtTokenUtil.validateToken(authToken, username) && Objects.equals(passwordV, rv) + && userDetails != null) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + authentication.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("adminUsername", jwt.getPayload("username")); + request.setAttribute("adminUserId", jwt.getPayload("userId")); + request.setAttribute("tokenInfo", jwt); + } + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/security/JwtSecurityConfig.java b/cool-admin-java/src/main/java/com/cool/core/security/JwtSecurityConfig.java new file mode 100644 index 0000000..6f9e722 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/JwtSecurityConfig.java @@ -0,0 +1,152 @@ +package com.cool.core.security; + +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.enums.UserTypeEnum; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.util.DigestUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.util.pattern.PathPattern; + +@EnableWebSecurity +@Configuration +@Slf4j +@RequiredArgsConstructor +public class JwtSecurityConfig { + + // 用户详情 + final private UserDetailsService userDetailsService; + final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + // 401 + final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler; + // 403 + final private RestAccessDeniedHandler restAccessDeniedHandler; + // 忽略权限控制的地址 + final private IgnoredUrlsProperties ignoredUrlsProperties; + + final private RequestMappingHandlerMapping requestMappingHandlerMapping; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + // 动态获取忽略的URL + configureIgnoredUrls(); + + return httpSecurity + .authorizeHttpRequests( + conf -> { + conf.requestMatchers( + ignoredUrlsProperties.getAdminAuthUrls().toArray(String[]::new)) + .permitAll(); + conf.requestMatchers("/admin/**").authenticated(); + conf.requestMatchers("/app/**").hasRole(UserTypeEnum.APP.name()); + }) + .headers(config -> config.frameOptions(FrameOptionsConfig::disable)) + // 允许网页iframe + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .addFilterBefore(jwtAuthenticationTokenFilter, + UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(config -> { + config.authenticationEntryPoint(entryPointUnauthorizedHandler); + config.accessDeniedHandler(restAccessDeniedHandler); + }).build(); + } + + private void configureIgnoredUrls() { + Map mappings = requestMappingHandlerMapping.getHandlerMethods(); + List handlerCtr = new ArrayList<>(); + mappings.forEach((requestMappingInfo, handlerMethod) -> { + Method method = handlerMethod.getMethod(); + TokenIgnore tokenIgnore = AnnotatedElementUtils.findMergedAnnotation(method, TokenIgnore.class); + TokenIgnore tokenIgnoreCtr = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), TokenIgnore.class); + if (!handlerCtr.contains(handlerMethod.getBeanType().getName()) && tokenIgnoreCtr != null) { + requestMappingInfo.getPathPatternsCondition().getPatterns().forEach(pathPattern -> { + String[] prefixs = pathPattern.getPatternString().split("/"); + // 去除最后一个路径 + List urls = new ArrayList<>(); + for (int i = 0; i < prefixs.length - 1; i++) { + urls.add(prefixs[i]); + } + // 遍历 tokenIgnoreCtr.value() + for (String path : tokenIgnoreCtr.value()) { + ignoredUrlsProperties.getAdminAuthUrls().add(String.join("/", urls) + "/" + path); + } + if (tokenIgnoreCtr.value().length == 0) { + // 通配 + ignoredUrlsProperties.getAdminAuthUrls().add(String.join("/", urls)+ "/**"); + } + handlerCtr.add(handlerMethod.getBeanType().getName()); + }); + } + if (tokenIgnore != null) { + StringBuilder url = new StringBuilder(); + RequestMapping classRequestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), RequestMapping.class); + if (classRequestMapping != null) { + for (String path : classRequestMapping.value()) { + url.append(path); + } + } + if (requestMappingInfo.getPathPatternsCondition() == null) { + return; + } + for (PathPattern path : requestMappingInfo.getPathPatternsCondition().getPatterns()) { + url.append(path); + } + ignoredUrlsProperties.getAdminAuthUrls().add(url.toString()); + } + }); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new PasswordEncoder() { + @Override + public String encode(CharSequence rawPassword) { + return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + return encodedPassword.equals( + DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes())); + } + }; + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) + throws Exception { + return config.getAuthenticationManager(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/security/MyAccessDecisionManager.java b/cool-admin-java/src/main/java/com/cool/core/security/MyAccessDecisionManager.java new file mode 100644 index 0000000..3c410af --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/MyAccessDecisionManager.java @@ -0,0 +1,61 @@ +package com.cool.core.security; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.FilterInvocation; +import org.springframework.stereotype.Component; + +/** + * 权限管理决断器 判断用户拥有的权限或角色是否有资源访问权限 + */ +@RequiredArgsConstructor +@Slf4j +@Component +public class MyAccessDecisionManager implements AccessDecisionManager { + // 忽略权限控制的地址 + final private IgnoredUrlsProperties ignoredUrlsProperties; + + @Override + public void decide(Authentication authentication, Object o, Collection configAttributes) + throws AccessDeniedException, InsufficientAuthenticationException { + if (configAttributes == null) { + return; + } + List urls = ignoredUrlsProperties.getAdminAuthUrls(); + String url = ((FilterInvocation) o).getRequestUrl().split("[?]")[0]; + if (urls.contains(url)) { + return; + } + Iterator iterator = configAttributes.iterator(); + while (iterator.hasNext()) { + ConfigAttribute c = iterator.next(); + String needPerm = c.getAttribute(); + for (GrantedAuthority ga : authentication.getAuthorities()) { + // 匹配用户拥有的ga 和 系统中的needPerm + if (needPerm.trim().equals(ga.getAuthority())) { + return; + } + } + } + throw new AccessDeniedException("抱歉,您没有访问权限"); + } + + @Override + public boolean supports(ConfigAttribute configAttribute) { + return true; + } + + @Override + public boolean supports(Class aClass) { + return true; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/security/MyFilterSecurityInterceptor.java b/cool-admin-java/src/main/java/com/cool/core/security/MyFilterSecurityInterceptor.java new file mode 100644 index 0000000..f22165b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/MyFilterSecurityInterceptor.java @@ -0,0 +1,68 @@ +package com.cool.core.security; + +import jakarta.annotation.Resource; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.SecurityMetadataSource; +import org.springframework.security.access.intercept.AbstractSecurityInterceptor; +import org.springframework.security.access.intercept.InterceptorStatusToken; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; +import org.springframework.stereotype.Component; + +/** + * 权限管理拦截器 监控用户行为 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { + + final private FilterInvocationSecurityMetadataSource securityMetadataSource; + + @Resource + public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) { + super.setAccessDecisionManager(myAccessDecisionManager); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + FilterInvocation fi = new FilterInvocation(request, response, chain); + invoke(fi); + } + + public void invoke(FilterInvocation fi) throws IOException, ServletException { + InterceptorStatusToken token = super.beforeInvocation(fi); + try { + fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); + } finally { + super.afterInvocation(token, null); + } + } + + @Override + public void destroy() { + } + + @Override + public Class getSecureObjectClass() { + return FilterInvocation.class; + } + + @Override + public SecurityMetadataSource obtainSecurityMetadataSource() { + return this.securityMetadataSource; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/security/RestAccessDeniedHandler.java b/cool-admin-java/src/main/java/com/cool/core/security/RestAccessDeniedHandler.java new file mode 100644 index 0000000..7954d05 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/RestAccessDeniedHandler.java @@ -0,0 +1,34 @@ +package com.cool.core.security; + +import cn.hutool.json.JSONUtil; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; + +/** + * 自定403返回值 + */ +@Component +public class RestAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) + throws IOException { + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + response.getWriter().write(JSONUtil.toJsonStr(new HashMap() { + { + put("code", "403"); + put("message", "无权限"); + } + })); + response.setStatus(403); + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtTokenUtil.java b/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtTokenUtil.java new file mode 100644 index 0000000..5335726 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtTokenUtil.java @@ -0,0 +1,165 @@ +package com.cool.core.security.jwt; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.jwt.JWT; +import cn.hutool.jwt.JWTUtil; +import cn.hutool.jwt.JWTValidator; +import com.cool.core.config.CoolProperties; +import com.cool.modules.base.service.sys.BaseSysConfService; +import java.io.Serializable; +import java.util.Date; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * JWT工具类 + */ +@Component +@RequiredArgsConstructor +public class JwtTokenUtil implements Serializable { + + final private CoolProperties coolProperties; + final private BaseSysConfService baseSysConfService; + final String tokenKey = "JWT_SECRET_TOKEN"; + final String refreshTokenKey = "JWT_SECRET_REFRESH_TOKEN"; + + public long getExpire() { + return this.coolProperties.getToken().getExpire(); + } + + public long getRefreshExpire() { + return this.coolProperties.getToken().getRefreshExpire(); + } + + public String getTokenSecret() { + String secret = baseSysConfService.getValueWithCache(tokenKey); + if (StrUtil.isBlank(secret)) { + secret = StrUtil.uuid().replaceAll("-", ""); + baseSysConfService.setValue(tokenKey, secret); + } + return secret; + } + + public String getRefreshTokenSecret() { + String secret = baseSysConfService.getValueWithCache(refreshTokenKey); + if (StrUtil.isBlank(secret)) { + secret = StrUtil.uuid().replaceAll("-", ""); + baseSysConfService.setValue(refreshTokenKey, secret); + } + return secret; + } + + /** + * 生成令牌 + * + * @param tokenInfo 保存的用户信息 + * @return 令牌 + */ + public String generateToken(Map tokenInfo) { + tokenInfo.put("isRefresh", false); + Date expirationDate = new Date(System.currentTimeMillis() + getExpire() * 1000); + JWT jwt = JWT.create().setExpiresAt(expirationDate).setKey(getTokenSecret().getBytes()) + .setPayload("created", new Date()); + tokenInfo.forEach(jwt::setPayload); + return jwt.sign(); + } + + /** + * 生成令牌 + * + * @param tokenInfo 保存的用户信息 + * @return 令牌 + */ + public String generateRefreshToken(Map tokenInfo) { + tokenInfo.put("isRefresh", true); + Date expirationDate = new Date(System.currentTimeMillis() + getRefreshExpire() * 1000); + JWT jwt = JWT.create().setExpiresAt(expirationDate).setKey(getRefreshTokenSecret().getBytes()) + .setPayload("created", new Date()); + tokenInfo.forEach(jwt::setPayload); + return jwt.sign(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) { + JWT jwt = JWT.of(token); + return jwt.getPayload("username").toString(); + } + + /** + * 获得token信息 + * + * @param token 令牌 + * @return token信息 + */ + public JWT getTokenInfo(String token) { + return JWT.of(token); + } + + /** + * 判断令牌是否过期 + * + * @param token 令牌 + * @return 是否过期 + */ + public Boolean isTokenExpired(String token) { + try { + JWTValidator.of(token).validateDate(DateUtil.date()); + return false; + } catch (Exception e) { + return true; + } + } + + /** + * 验证令牌 + * + * @param token 令牌 + * @param username 用户 + * @return 是否有效 + */ + public Boolean validateToken(String token, String username) { + if (ObjectUtil.isEmpty(token)) { + return false; + } + String tokenUsername = getUsernameFromToken(token); + String secret = getTokenSecret(); + boolean isValidSignature = JWTUtil.verify(token, secret.getBytes()); + return (tokenUsername.equals(username) && !isTokenExpired(token) && isValidSignature); + } + + /** + * 校验token是否有效 + * @param token + * @return + */ + public Boolean validateToken(String token) { + if (ObjectUtil.isEmpty(token)) { + return false; + } + String secret = getTokenSecret(); + boolean isValidSignature = JWTUtil.verify(token, secret.getBytes()); + return (!isTokenExpired(token) && isValidSignature); + } + + /** + * 校验refresh token是否有效 + * @param token + * @return + */ + public Boolean validateRefreshToken(String token) { + if (ObjectUtil.isEmpty(token)) { + return false; + } + String secret = getRefreshTokenSecret(); + boolean isValidSignature = JWTUtil.verify(token, secret.getBytes()); + return (!isTokenExpired(token) && isValidSignature); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtUser.java b/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtUser.java new file mode 100644 index 0000000..c69cf9e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/security/jwt/JwtUser.java @@ -0,0 +1,78 @@ +package com.cool.core.security.jwt; + +import com.cool.core.enums.UserTypeEnum; +import java.util.Collection; +import java.util.List; +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * 后台用户信息 + */ +@Data +public class JwtUser implements UserDetails { + + /****** + * 后台用户 + * ********/ + private Long userId; + private String username; + private String password; + private Boolean status; + private UserTypeEnum userTypeEnum; + private List perms; + public JwtUser(Long userId, String username, String password, List perms, Boolean status) { + this.userId = userId; + this.username = username; + this.password = password; + this.perms = perms; + this.status = status; + this.userTypeEnum = UserTypeEnum.ADMIN; + } + + /****** + * app用户 + * ********/ + public JwtUser(Long userId, List perms, Boolean status) { + this.userId = userId; + this.perms = perms; + this.status = status; + this.userTypeEnum = UserTypeEnum.APP; + } + + @Override + public Collection getAuthorities() { + return perms; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return status; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/tenant/CoolTenantFactory.java b/cool-admin-java/src/main/java/com/cool/core/tenant/CoolTenantFactory.java new file mode 100644 index 0000000..f79ebef --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/tenant/CoolTenantFactory.java @@ -0,0 +1,14 @@ +package com.cool.core.tenant; + +import com.cool.core.util.TenantUtil; +import com.mybatisflex.core.tenant.TenantFactory; + +public class CoolTenantFactory implements TenantFactory { + public Object[] getTenantIds(){ + Long tenantId = TenantUtil.getTenantId(); + if (tenantId == null) { + return null; + } + return new Object[]{tenantId}; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/AnnotationUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/AnnotationUtils.java new file mode 100644 index 0000000..b65d6a3 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/AnnotationUtils.java @@ -0,0 +1,73 @@ +package com.cool.core.util; + +import com.cool.core.annotation.CoolPlugin; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; + +@Slf4j +public class AnnotationUtils { + + /** + * 判断一个类是否有 Spring 核心注解 + * + * @param clazz 要检查的类 + * @return true 如果该类上添加了相应的 Spring 注解;否则返回 false + */ + public static boolean hasSpringAnnotation(Class clazz) { + if (clazz == null) { + return false; + } + // 是否是接口 + if (clazz.isInterface()) { + return false; + } + // 是否是抽象类 + if (Modifier.isAbstract(clazz.getModifiers())) { + return false; + } + + try { + if (clazz.getAnnotation(Component.class) != null || clazz.getAnnotation(Repository.class) != null + || clazz.getAnnotation(Service.class) != null || clazz.getAnnotation(Controller.class) != null + || clazz.getAnnotation(Configuration.class) != null) { + return true; + } + } catch (Exception e) { + log.error("出现异常:{}", e.getMessage()); + } + return false; + } + + /** + * 插件 + */ + public static boolean hasCoolPluginAnnotation(Class clazz) { + if (clazz == null) { + return false; + } + // 是否是接口 + if (clazz.isInterface()) { + return false; + } + // 是否是抽象类 + if (Modifier.isAbstract(clazz.getModifiers())) { + return false; + } + try { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (clazz.getAnnotation( + (Class) contextClassLoader.loadClass(CoolPlugin.class.getName())) != null) { + return true; + } + } catch (Exception e) { + log.error("出现异常:{}", e.getMessage(), e); + } + return false; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/util/AutoTypeConverter.java b/cool-admin-java/src/main/java/com/cool/core/util/AutoTypeConverter.java new file mode 100644 index 0000000..45ed11c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/AutoTypeConverter.java @@ -0,0 +1,27 @@ +package com.cool.core.util; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.NumberUtil; + +import java.io.Serializable; + +public class AutoTypeConverter { + /** + * 将字符串自动转换为数字或保留为字符串 + * + * @param input 输入字符串 + * @return Integer / Long / String + */ + public static Serializable autoConvert(Object input) { + if (input == null) { + return null; + } + if (NumberUtil.isInteger(input.toString())) { + return Convert.convert(Integer.class, input); + } else if (NumberUtil.isLong(input.toString())) { + return Convert.convert(Long.class, input); + } else { + return (Serializable) input; + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/BodyReaderHttpServletRequestWrapper.java b/cool-admin-java/src/main/java/com/cool/core/util/BodyReaderHttpServletRequestWrapper.java new file mode 100644 index 0000000..2c783d2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/BodyReaderHttpServletRequestWrapper.java @@ -0,0 +1,119 @@ +package com.cool.core.util; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.nio.charset.Charset; + +/** + * 保存流 + */ +@Slf4j +public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final byte[] body; + + public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + String sessionStream = getBodyString(request); + body = sessionStream.getBytes(Charset.forName("UTF-8")); + } + + /** + * 获取请求Body + * + * @param request + * @return + */ + public String getBodyString(final ServletRequest request) { + StringBuilder sb = new StringBuilder(); + InputStream inputStream = null; + BufferedReader reader = null; + try { + inputStream = cloneInputStream(request.getInputStream()); + reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); + String line = ""; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + log.error("err", e); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + log.error("err", e); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + log.error("err", e); + } + } + } + return sb.toString(); + } + + /** + * Description: 复制输入流
+ * + * @param inputStream + * @return
+ */ + public InputStream cloneInputStream(ServletInputStream inputStream) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + try { + while ((len = inputStream.read(buffer)) > -1) { + byteArrayOutputStream.write(buffer, 0, len); + } + byteArrayOutputStream.flush(); + } catch (IOException e) { + log.error("err", e); + } + InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + return byteArrayInputStream; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + + return new ServletInputStream() { + + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + }; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/CompilerUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/CompilerUtils.java new file mode 100644 index 0000000..3e6506f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/CompilerUtils.java @@ -0,0 +1,180 @@ +package com.cool.core.util; + +import cn.hutool.core.io.FileUtil; +import com.mybatisflex.processor.MybatisFlexProcessor; +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.nio.charset.StandardCharsets; +import java.util.List; +import javax.annotation.processing.Processor; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class CompilerUtils { + + public final static String META_INF_VERSIONS = "META-INF/versions/"; + + // jdk版本 + private static String JVM_VERSION = null; + + /** + * 获取jdk版本 + */ + public static String getJdkVersion() { + if (JVM_VERSION == null) { + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + JVM_VERSION = runtimeMXBean.getSpecVersion(); + } + return JVM_VERSION; + } + + /** + * 创建文件, 先删除在创建 + */ + public static void createFile(String content, String filePathStr) { + FileUtil.del(filePathStr); + File file = FileUtil.touch(filePathStr); + FileUtil.appendString(content, file, StandardCharsets.UTF_8.name()); + compileAndSave(filePathStr); + } + + public static String createMapper(String actModulePath, String fileName, String mapper) { + String pathStr = actModulePath + File.separator + "mapper" + File.separator; + String filePathStr = pathStr + fileName + "Mapper.java"; + createFile(mapper, filePathStr); + return filePathStr; + } + + public static String createServiceImpl(String actModulePath, String fileName, + String serviceImpl) { + String pathStr = actModulePath + File.separator + "service" + File.separator + "impl" + File.separator; + String filePathStr = pathStr + fileName + "ServiceImpl.java"; + createFile(serviceImpl, filePathStr); + return filePathStr; + } + + public static String createService(String actModulePath, String fileName, String service) { + String pathStr = actModulePath + File.separator + "service" + File.separator; + String filePathStr = pathStr + fileName + "Service.java"; + createFile(service, filePathStr); + return filePathStr; + } + + public static String createEntity(String actModulePath, String fileName, String entity) { + String pathStr = actModulePath + File.separator + "entity" + File.separator; + String filePathStr = pathStr + fileName + "Entity.java"; + createFile(entity, filePathStr); + return filePathStr; + } + + public static String createController(String actModulePath, String fileName, String controller) { + String pathStr = actModulePath + File.separator + "controller" + File.separator + "admin" + File.separator; + String filePathStr = pathStr + "Admin" + fileName + "Controller.java"; + createFile(controller, filePathStr); + return filePathStr; + } + + public static String createModule(String modulesPath, String module) { + String pathStr = modulesPath + File.separator + module; + PathUtils.noExistsMk(pathStr); + return pathStr; + } + + public static boolean compileAndSave(String sourceFile) { + // 获取系统 Java 编译器 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + + // 获取标准文件管理器 + try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { + + // 设置编译输出目录 + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(new File("target" + File.separator + "classes"))); + + // 获取源文件 + List javaFiles = List.of(new File(sourceFile)); + Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles(javaFiles); + + // 创建编译任务 + JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits); + // 执行编译任务 + return task.call(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + public static void compilerEntityTableDef(String actModulePath, String fileName, String entityPath, List javaPathList) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { + Iterable compilationUnits = fileManager.getJavaFileObjects( + entityPath); + // 设置注解处理器 + Iterable processors = List.of(new MybatisFlexProcessor()); + // 添加 -proc:only 选项 + List options = List.of("-proc:only"); + JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, + null, compilationUnits); + task.setProcessors(processors); + task.call(); + compilationUnits = fileManager.getJavaFileObjects( + javaPathList.toArray(new String[0])); + // 设置编译输出目录 + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(new File("target/classes"))); + + task = compiler.getTask(null, fileManager, null, null, null, compilationUnits); + String pathStr = actModulePath + File.separator + "entity" + File.separator + "table" + File.separator; + String filePathStr = pathStr + fileName + "EntityTableDef.java"; + // 需在entity之后加载 + javaPathList.add(1, filePathStr); + boolean success = task.call(); + if (success) { + System.out.println("Compilation and annotation processing completed successfully."); + // 指定源文件夹和目标文件夹 + File sourceDir = new File("com"); + File destinationDir = new File(PathUtils.getTargetGeneratedAnnotations()); + // 确保目标文件夹存在 + destinationDir.mkdirs(); + // 移动源文件夹内容到目标文件夹 + if (sourceDir.exists()) { + FileUtil.move(sourceDir, destinationDir, true); + } + if (countFiles(sourceDir) <= 1) { + FileUtil.clean(sourceDir); + FileUtil.del(sourceDir); + } + } else { + System.out.println("Compilation and annotation processing failed."); + } + } catch (IOException e) { + log.error("compilerEntityTableDefError", e); + } + } + private static int countFiles(File directory) { + File[] files = directory.listFiles(); + if (files == null) { + return 0; + } + + int count = 0; + for (File file : files) { + if (file.isFile()) { + count++; + } else if (file.isDirectory()) { + count += countFiles(file); + } + // If more than one file is found, no need to continue counting + if (count > 1) { + break; + } + } + return count; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/ConvertUtil.java b/cool-admin-java/src/main/java/com/cool/core/util/ConvertUtil.java new file mode 100644 index 0000000..be72f86 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/ConvertUtil.java @@ -0,0 +1,257 @@ +package com.cool.core.util; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.*; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.springframework.web.multipart.MultipartFile; + +/** + * 转换 + */ +public class ConvertUtil { + + /** + * 对象转数组 + * + * @param obj + * @return + */ + public static byte[] toByteArray(Object obj) { + byte[] bytes = null; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(obj); + oos.flush(); + bytes = bos.toByteArray(); + oos.close(); + bos.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + return bytes; + } + + /** + * 数组转对象 + * + * @param bytes + * @return + */ + public static Object toObject(byte[] bytes) { + Object obj = null; + try { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + obj = ois.readObject(); + ois.close(); + bis.close(); + } catch (IOException | ClassNotFoundException ex) { + ex.printStackTrace(); + } + return obj; + } + + public static MultipartFile convertToMultipartFile(File file) { + FileInputStream inputStream = null; + try { + inputStream = new FileInputStream(file); + return new SimpleMultipartFile(file.getName(), inputStream); + } catch (FileNotFoundException e) { + e.printStackTrace(); + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return null; + } + } + + // 简单的MultipartFile实现,用于模拟Spring中的MultipartFile对象 + static class SimpleMultipartFile implements MultipartFile { + + private String filename; + private InputStream inputStream; + + public SimpleMultipartFile(String filename, InputStream inputStream) { + this.filename = filename; + this.inputStream = inputStream; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getOriginalFilename() { + return filename; + } + + @Override + public String getContentType() { + return "application/octet-stream"; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public long getSize() { + try { + return inputStream.available(); + } catch (IOException e) { + e.printStackTrace(); + return 0; + } + } + + @Override + public byte[] getBytes() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + output.write(buffer, 0, len); + } + return output.toByteArray(); + } + + @Override + public InputStream getInputStream() throws IOException { + return inputStream; + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + try (FileOutputStream outputStream = new FileOutputStream(dest)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + } + + + + /** + * /admin/goods 转 AdminGoods + */ + public static String pathToClassName(String path) { + // 按斜杠分割字符串 + String[] parts = path.split("/"); + StringBuilder className = new StringBuilder(); + for (String part : parts) { + // 将每个部分的首字母大写,并追加到 StringBuilder 中 + className.append(StrUtil.upperFirst(part)); + } + return className.toString(); + } + + public static String extractController2Path(String prefix, String className) { + Pattern pattern = Pattern.compile("([A-Za-z0-9]+)Controller$"); + Matcher matcher = pattern.matcher(className); + + if (matcher.find()) { + String extracted = matcher.group(1); + + // 将前缀拆分为单词数组 + String[] prefixWords = splitCamelCase(prefix); + String[] classWords = splitCamelCase(extracted); + + // 从前缀和类名中逐个匹配并去除匹配的部分 + int i = 0; + for (int j = 0; i < prefixWords.length; j++) { + if (j >= classWords.length) { + break; + } + for (String prefixWord : prefixWords) { + if (prefixWord.equalsIgnoreCase(classWords[i])) { + i++; + break; + } + } + } + // 从当前位置开始,拼接剩余部分 + return String.join("/", java.util.Arrays.copyOfRange(classWords, i, classWords.length)).toLowerCase(); + } + return ""; + } + + // 拆分驼峰命名的字符串为单词数组 + private static String[] splitCamelCase(String input) { + return input.split("(?<=.)(?=[A-Z])"); + } + + /** + * 将给定的字段值转换为可序列化的形式 + * 此方法旨在将一个对象的特定字段值转换为其相应的可序列化类型 + * 它在序列化和反序列化过程中特别有用,确保字段值可以被正确处理 + * + * @param fieldName 字段名称,用于查找字段类型 + * @param fieldValue 待转换的字段值 + * @param clazz 包含该字段的类 + * @return 转换后的可序列化字段值,如果无法确定字段类型,则返回原始值 + */ + public static Object convertByClass(String fieldName, Object fieldValue, Class clazz) { + // 检查输入参数是否为空,如果字段名或字段值为空,则直接返回字段值 + if (fieldName == null || fieldValue == null) { + return fieldValue; + } + + // 获取字段类型 + Class fieldType = getFieldType(clazz, fieldName); + // 如果字段类型为空,则直接返回字段值 + if (fieldType == null) { + return fieldValue; + } + + // 使用Convert类的convert方法将字段值转换为字段类型 + return Convert.convert(fieldType, fieldValue); + } + + public static List covertListByClass(String fieldName, List fieldValue, Class clazz) { + // 检查输入参数是否为空,如果字段名或字段值为空,则直接返回字段值 + if (fieldName == null || fieldValue == null) { + return fieldValue; + } + + // 获取字段类型 + Class fieldType = getFieldType(clazz, fieldName); + // 如果字段类型为空,则直接返回字段值 + if (fieldType == null) { + return fieldValue; + } + + return Collections.singletonList(Convert.toList(fieldType, fieldValue)); + } + /** + * 获取指定类中指定字段的类型 + * + * @param clazz 目标类 + * @param fieldName 字段名称 + * @return 字段的类型 Class,如果字段不存在则返回 null + */ + public static Class getFieldType(Class clazz, String fieldName) { + Field field = ReflectUtil.getField(clazz, fieldName); + return field != null ? field.getType() : null; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/util/CoolPluginInvokers.java b/cool-admin-java/src/main/java/com/cool/core/util/CoolPluginInvokers.java new file mode 100644 index 0000000..2a2fdf2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/CoolPluginInvokers.java @@ -0,0 +1,165 @@ +package com.cool.core.util; + +import cn.hutool.json.JSONUtil; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.plugin.consts.PluginConsts; +import com.cool.core.plugin.service.DynamicJarLoaderService; +import com.cool.modules.plugin.entity.PluginInfoEntity; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; + +/** + * 插件调用封装 + */ +@Slf4j +public class CoolPluginInvokers { + + private static final DynamicJarLoaderService dynamicJarLoaderService = SpringContextUtils + .getBean(DynamicJarLoaderService.class); + + /** + * 插件默认调用入口 + */ + public static Object invokePlugin(String key, String... params) { + return invoke(key, PluginConsts.invokePluginMethodName, params); + } + + /** + * 设置插件配置信息 + */ + public static void setPluginJson(String key, PluginInfoEntity entity) { + invoke(key, PluginConsts.setPluginJson, JSONUtil.toJsonStr(entity.getPluginJson())); + setApplicationContext(key); + } + + /** + * 设置 ApplicationContext 到插件类中 + */ + public static void setApplicationContext(String key) { + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread() + .setContextClassLoader(dynamicJarLoaderService.getDynamicJarClassLoader(key)); + Object beanInstance = dynamicJarLoaderService.getBeanInstance(key); + Method method = beanInstance.getClass().getSuperclass() + .getMethod(PluginConsts.setApplicationContext, + ApplicationContext.class); + method.invoke(beanInstance, SpringContextUtils.applicationContext); + } catch (Exception e) { + log.error("setApplicationContext err", e); + } finally { + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + } + + /** + * 反射调用插件 + * + * @param key 插件key + * @param methodName 插件方法 + * @param params 参数 + */ + public static Object invoke(String key, String methodName, Object... params) { + Object beanInstance = dynamicJarLoaderService.getBeanInstance(key); + CoolPreconditions.checkEmpty(beanInstance, "未找到该插件:{}, 请前往插件市场进行安装",key); + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + try { + // 设置当前线程的上下文类加载器为插件的类加载器 + Thread.currentThread() + .setContextClassLoader(dynamicJarLoaderService.getDynamicJarClassLoader(key)); + log.info("调用插件类: {}, 方法: {} 参数: {}", key, methodName, params); + return invoke(beanInstance, methodName, params); + } catch (Exception e) { + log.error("调用插件{}.{}失败", key, methodName, e); + CoolPreconditions.alwaysThrow("调用插件{}.{}失败 {}", key, methodName, e.getMessage()); + } finally { + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + return null; + } + + /** + * 反射调用插件 + * + * @param beanInstance 插件实例对象 + * @param methodName 插件方法 + * @param params 参数 + */ + private static Object invoke(Object beanInstance, String methodName, Object[] params) + throws InvocationTargetException, IllegalAccessException { + Class[] paramTypes = Arrays.stream(params).map(Object::getClass) + .toArray(Class[]::new); + Method method = findMethod(beanInstance.getClass(), methodName, paramTypes); + CoolPreconditions.check(method == null, "No such method: {} with parameters {}", methodName, + Arrays.toString(paramTypes)); + if (method.isVarArgs()) { + // 处理可变参数调用 + int varArgIndex = method.getParameterTypes().length - 1; + Object[] varArgs = (Object[]) java.lang.reflect.Array.newInstance( + method.getParameterTypes()[varArgIndex].getComponentType(), + params.length - varArgIndex); + System.arraycopy(params, varArgIndex, varArgs, 0, varArgs.length); + Object[] methodArgs = new Object[varArgIndex + 1]; + System.arraycopy(params, 0, methodArgs, 0, varArgIndex); + methodArgs[varArgIndex] = varArgs; + return method.invoke(beanInstance, methodArgs); + } else { + // 正常调用 + return method.invoke(beanInstance, params); + } + } + + // 查找方法,包括处理可变参数 + private static Method findMethod(Class clazz, String methodName, Class... paramTypes) { + try { + return clazz.getMethod(methodName, paramTypes); + } catch (NoSuchMethodException e) { + // Try to find a varargs method + for (Method method : clazz.getMethods()) { + if (method.getName().equals(methodName) && isAssignable(paramTypes, + method.getParameterTypes(), method.isVarArgs())) { + return method; + } + } + // If not found, try to find in superclass + if (clazz.getSuperclass() != null) { + return findMethod(clazz.getSuperclass(), methodName, paramTypes); + } + } + return null; + } + + private static boolean isAssignable(Class[] paramTypes, Class[] methodParamTypes, + boolean isVarArgs) { + if (isVarArgs) { + if (paramTypes.length < methodParamTypes.length - 1) { + return false; + } + for (int i = 0; i < methodParamTypes.length - 1; i++) { + if (!methodParamTypes[i].isAssignableFrom(paramTypes[i])) { + return false; + } + } + Class varArgType = methodParamTypes[methodParamTypes.length - 1].getComponentType(); + for (int i = methodParamTypes.length - 1; i < paramTypes.length; i++) { + if (!varArgType.isAssignableFrom(paramTypes[i])) { + return false; + } + } + return true; + } else { + if (paramTypes.length != methodParamTypes.length) { + return false; + } + for (int i = 0; i < paramTypes.length; i++) { + if (!methodParamTypes[i].isAssignableFrom(paramTypes[i])) { + return false; + } + } + return true; + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/CoolSecurityUtil.java b/cool-admin-java/src/main/java/com/cool/core/util/CoolSecurityUtil.java new file mode 100644 index 0000000..5173cca --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/CoolSecurityUtil.java @@ -0,0 +1,110 @@ +package com.cool.core.util; + +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.security.jwt.JwtUser; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * Security 工具类 + */ +public class CoolSecurityUtil { + + private static final CoolCache coolCache = SpringUtil.getBean(CoolCache.class); + + /***************后台********************/ + /** + * 获取后台登录的用户名 + */ + public static String getAdminUsername() { + return SecurityContextHolder.getContext().getAuthentication().getName(); + } + + /** + * 获得jwt中的信息 + * + * @param requestParams 请求参数 + * @return jwt + */ + public static JSONObject getAdminUserInfo(JSONObject requestParams) { + JSONObject tokenInfo = requestParams.getJSONObject("tokenInfo"); + if (tokenInfo != null) { + tokenInfo.set("department", + coolCache.get("admin:department:" + tokenInfo.get("userId"))); + tokenInfo.set("roleIds", coolCache.get("admin:roleIds:" + tokenInfo.get("userId"))); + } + return tokenInfo; + } + + public static Long getTenantId(JSONObject requestParams) { + JSONObject tokenInfo = requestParams.getJSONObject("tokenInfo"); + if (tokenInfo != null) { + return tokenInfo.getLong("tenantId"); + } + return null; + } + + /** + * 后台账号退出登录 + * + * @param adminUserId 用户ID + * @param username 用户名 + */ + public static void adminLogout(Long adminUserId, String username) { + coolCache.del("admin:department:" + adminUserId, "admin:passwordVersion:" + adminUserId, + "admin:userInfo:" + adminUserId, "admin:userDetails:" + username); + } + + /** + * 后台账号退出登录 + * + * @param userEntity 用户 + */ + public static void adminLogout(BaseSysUserEntity userEntity) { + adminLogout(userEntity.getId(), userEntity.getUsername()); + } + + + /** + * 获取当前用户id + */ + public static Long getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + Object principal = authentication.getPrincipal(); + if (principal instanceof UserDetails) { + return ((JwtUser) principal).getUserId(); + } + } + CoolPreconditions.check(true, 401, "未登录"); + return null; + } + + /** + * 获取当前用户类型 + */ + public static UserTypeEnum getCurrentUserType() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + Object principal = authentication.getPrincipal(); + if (principal instanceof UserDetails) { + return ((JwtUser) principal).getUserTypeEnum(); + } + } + // 还未登录,未知类型 + return UserTypeEnum.UNKNOWN; + } + + /** + * app退出登录,移除缓存信息 + */ + public static void appLogout() { + coolCache.del("app:userDetails"+ getCurrentUserId()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/DatabaseDialectUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/DatabaseDialectUtils.java new file mode 100644 index 0000000..f4f2554 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/DatabaseDialectUtils.java @@ -0,0 +1,56 @@ +package com.cool.core.util; +import com.cool.core.exception.CoolPreconditions; +import org.dromara.autotable.core.constants.DatabaseDialect; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import javax.sql.DataSource; + +/** + * 获取数据库方言 + */ +public class DatabaseDialectUtils { + private static String dialect; + + public static String getDatabaseDialect(DataSource dataSource) { + if (dialect == null) { + dialect = determineDatabaseType(dataSource); + } + return dialect; + } + + public static boolean isPostgresql() { + DataSource dataSource = SpringContextUtils.getBean(DataSource.class); + return DatabaseDialect.PostgreSQL.equals(getDatabaseDialect(dataSource)); + } + + public static boolean isPostgresql(DataSource dataSource) { + return DatabaseDialect.PostgreSQL.equals(getDatabaseDialect(dataSource)); + } + + + private static String determineDatabaseType(DataSource dataSource) { + // 从 DataSource 获取连接 + try (Connection connection = dataSource.getConnection()) { + // 获取元数据 + DatabaseMetaData metaData = connection.getMetaData(); + String productName = metaData.getDatabaseProductName(); + + return inferDatabaseTypeFromProductName(productName); + } catch (SQLException e) { + throw new RuntimeException("Failed to determine database dialect", e); + } + } + + private static String inferDatabaseTypeFromProductName(String productName) { + if (productName.startsWith(DatabaseDialect.MySQL)) { + return DatabaseDialect.MySQL; + } else if (productName.startsWith(DatabaseDialect.PostgreSQL)) { + return DatabaseDialect.PostgreSQL; + } else if (productName.startsWith(DatabaseDialect.SQLite)) { + return DatabaseDialect.SQLite; + } + CoolPreconditions.alwaysThrow("暂不支持!"); + return "unknown"; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/EntityUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/EntityUtils.java new file mode 100644 index 0000000..b564a0c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/EntityUtils.java @@ -0,0 +1,121 @@ +package com.cool.core.util; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Editor; +import cn.hutool.core.util.ObjUtil; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.query.QueryColumn; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +public class EntityUtils { + + private static Map> TABLE_MAP; + + public static Set findEntityClassName() { + Set entitySet = new HashSet<>(); + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = null; + try { + resources = resolver.getResources("classpath*:com/cool/**/entity/**/*Entity.class"); + for (Resource r : resources) { + String path = r.getURL().getPath(); + String className = path.substring(path.indexOf("com/cool"), + path.lastIndexOf('.')).replace('/', '.'); + entitySet.add(className); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return entitySet; + } + + public static Map> findTableMap() { + if (ObjUtil.isEmpty(TABLE_MAP)) { + init(); + } + return TABLE_MAP; + } + + private static void init() { + Set classNames = EntityUtils.findEntityClassName(); + TABLE_MAP = new HashMap<>(); + classNames.forEach(className -> { + Class entityClass; + try { + entityClass = Class.forName(className); + Table tableAnnotation = AnnotationUtil.getAnnotation(entityClass, Table.class); + // key表名,value 实体对象 + TABLE_MAP.put(tableAnnotation.value(), entityClass); + } catch (Exception e) { + // do nothing + } + }); + } + + /** + * 获取实体类及其父类的字段名数组(排除指定字段) + * + * @return 字段名数组 + */ + public static QueryColumn[] getFieldNamesWithSuperClass(QueryColumn[] queryColumns, + String... excludeNames) { + return getFieldNamesListWithSuperClass(queryColumns, excludeNames).toArray( + new QueryColumn[0]); + } + + public static List getFieldNamesListWithSuperClass(QueryColumn[] queryColumns, + String... excludeNames) { + ArrayList excludeList = new ArrayList<>(List.of(excludeNames)); + return Arrays.stream(queryColumns).toList().stream() + .filter(o -> !excludeList.contains(o.getName())).toList(); + } + + /** + * 将bean的部分属性转换成map
+ * 可选拷贝哪些属性值,默认是不忽略值为{@code null}的值的。 + * + * @param bean bean + * @param ignoreProperties 需要忽略拷贝的属性值,{@code null}或空表示拷贝所有值 + * @return Map + * @since 5.8.0 + */ + public static Map toMap(Object bean, String... ignoreProperties) { + int mapSize = 16; + Editor keyEditor = null; + final Set propertiesSet = CollUtil.set(false, ignoreProperties); + propertiesSet.add("queryWrapper"); + mapSize = ignoreProperties.length; + keyEditor = property -> !propertiesSet.contains(property) ? property : null; + // 指明了要复制的属性 所以不忽略null值 + return BeanUtil.beanToMap(bean, new LinkedHashMap<>(mapSize, 1), false, keyEditor); + } + + /** + * 检查字段名是否在排除列表中 + * + * @param fieldName 要检查的字段名 + * @param excludeNames 排除的字段名数组 + * @return 是否在排除列表中 + */ + private static boolean isExcluded(String fieldName, String[] excludeNames) { + for (String excludeName : excludeNames) { + if (fieldName.equals(excludeName)) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/util/I18nUtil.java b/cool-admin-java/src/main/java/com/cool/core/util/I18nUtil.java new file mode 100644 index 0000000..13d28d5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/I18nUtil.java @@ -0,0 +1,125 @@ +package com.cool.core.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +@Slf4j +@Component +public class I18nUtil { + + public static final String MSG_PREFIX = "msg_"; + public static final String MENU_PREFIX = "menu_"; + public static final String DICT_INFO_PREFIX = "dictInfo_"; + public static final String DICT_TYPE_PREFIX = "dictType_"; + + public static boolean enable = false; + + public static String path; + public static String getLanguage() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + if (attributes == null) { + return null; + } + return (String) attributes.getAttribute("cool-language", RequestAttributes.SCOPE_REQUEST); + } + + private static final Map data = new ConcurrentHashMap<>(); + + private void load(String key, File file) { + try { + String content = FileUtil.readUtf8String(file); + data.put(key, JSONUtil.parseObj(content)); + } catch (Exception e) { + log.error("读取国际化文件失败", e); + } + } + + public boolean exist(String name) { + // 获取该目录下所有的 .json 文件 + List jsonFiles = FileUtil.loopFiles(getPath(), file -> + file.isFile() && file.getName().endsWith(".json") + ); + AtomicReference flag = new AtomicReference<>(false); + jsonFiles.forEach(file -> { + String parentName = file.getParentFile().getName(); + String key = parentName + "_" + file.getName().replace(".json", ""); + if (key.equals(name)) { + flag.set(true); + // 加载 + load(key, file); + } + }); + return flag.get(); + } + + public static String getI18nMenu(String name) { + return getI18n(name, MENU_PREFIX); + } + + public static String getI18nMsg(String name) { + return getI18n(name, MSG_PREFIX); + } + + public static String getI18nDictInfo(String name) { + return getI18n(name, DICT_INFO_PREFIX); + } + public static String getI18nDictType(String name) { + return getI18n(name, DICT_TYPE_PREFIX); + } + private static String getI18n(String name, String prefix) { + if (!enable) { + return name; + } + String language = I18nUtil.getLanguage(); + if (language == null) { + return name; + } + JSONObject jsonObject = data.get(prefix + language); + if (jsonObject == null) { + return name; + } + String str = jsonObject.getStr(name); + if (str == null) { + return name; + } + return str; + } + + public void update(String key, JSONObject object) { + data.put(key, object); + String[] split = key.split("_"); + String absolutePath = getPath(); + File file = FileUtil.file(absolutePath, split[0], split[1] + ".json"); + // 确保父目录存在 + FileUtil.mkParentDirs(file); + // 写入内容 + FileUtil.writeUtf8String(JSONUtil.toJsonStr(object), file); + } + + private String getPath() { + String absolutePath = path; + if (!PathUtils.isAbsolutePath(absolutePath)) { + absolutePath = PathUtils.getUserDir() + File.separator + absolutePath; + } + return absolutePath; + } + + public void clear() { + data.clear(); + List jsonFiles = FileUtil.loopFiles(getPath(), file -> + file.isFile() && file.getName().endsWith(".json") + ); + jsonFiles.forEach(File::delete); + enable = false; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/util/IPUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/IPUtils.java new file mode 100644 index 0000000..ed33d80 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/IPUtils.java @@ -0,0 +1,46 @@ +package com.cool.core.util; + +import cn.hutool.core.util.StrUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + + +/** + * IP地址 + */ +@Slf4j +@Component +public class IPUtils { + + /** + * 获取IP地址 + *

+ * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 + */ + public String getIpAddr(HttpServletRequest request) { + String ip = null; + try { + ip = request.getHeader("x-forwarded-for"); + if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } catch (Exception e) { + log.error("IP extraction error", e); + } + return ip; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/MapExtUtil.java b/cool-admin-java/src/main/java/com/cool/core/util/MapExtUtil.java new file mode 100644 index 0000000..3179ea2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/MapExtUtil.java @@ -0,0 +1,30 @@ +package com.cool.core.util; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.util.Map; + +public class MapExtUtil extends MapUtil { + + /** + * 比较两个map key 和 value 是否一致 + */ + public static boolean compareMaps(Map map1, Map map2) { + if (ObjectUtil.isEmpty(map1) || ObjectUtil.isEmpty(map2)) { + return true; + } + if (map1.size() != map2.size()) { + return false; + } + for (Map.Entry entry : map1.entrySet()) { + if (!map2.containsKey(entry.getKey())) { + return false; + } + if (!ObjectUtil.equal(entry.getValue(), map2.get(entry.getKey()))) { + return false; + } + } + return true; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/MappingAlgorithm.java b/cool-admin-java/src/main/java/com/cool/core/util/MappingAlgorithm.java new file mode 100644 index 0000000..783aa77 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/MappingAlgorithm.java @@ -0,0 +1,21 @@ +package com.cool.core.util; + +/** + * 自定义映射算法 + * 将 ID 转换为一个混淆形式的数字,然后能够逆向转换回原始 ID。 + * 场景:混淆订单id + */ +public class MappingAlgorithm { + + private static final long ENCRYPTION_KEY = 123456789L; // 任意密钥 + + // 将 ID 转换为混淆的数字 + public static long encrypt(long id) { + return id ^ ENCRYPTION_KEY; // 使用异或操作进行混淆 + } + + // 将混淆的数字恢复为原始的 ID + public static long decrypt(long encryptedId) { + return encryptedId ^ ENCRYPTION_KEY; // 逆操作恢复原始 ID + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/PathUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/PathUtils.java new file mode 100644 index 0000000..0f61184 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/PathUtils.java @@ -0,0 +1,70 @@ +package com.cool.core.util; + +import cn.hutool.core.io.file.PathUtil; +import cn.hutool.core.text.AntPathMatcher; +import com.cool.CoolApplication; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class PathUtils { + private static final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + public static boolean isAbsolutePath(String pathStr) { + Path path = Paths.get(pathStr); + return path.isAbsolute(); + } + + public static String getUserDir() { + return System.getProperty("user.dir"); + } + + public static String getModulesPath() { + return getUserDir() + getSrcMainJava() + File.separator + CoolApplication.class.getPackageName() + .replace(".", File.separator) + File.separator + "modules"; + } + + public static String getSrcMainJava() { + return File.separator + "src" + File.separator + "main" + File.separator + "java"; + } + public static String getTargetGeneratedAnnotations() { + return "target" + File.separator + "generated-sources" + File.separator + "annotations"; + } + + public static String getClassName(String filePath) { + // 定位 "/src/main/java" 在路径中的位置 + int srcMainJavaIndex = filePath.indexOf(getSrcMainJava()); + if (srcMainJavaIndex == -1) { + throw new IllegalArgumentException("File path does not contain 'src/main/java'"); + } + + // 提取 "src/main/java" 之后的路径 + // 将文件分隔符替换为包分隔符 + return filePath.substring(srcMainJavaIndex + ("src" + File.separator + "main" + File.separator + "java").length() + 2) + .replace(File.separator, ".").replace(".java", ""); + } + + /** + * 路径不存在创建 + */ + public static void noExistsMk(String pathStr) { + Path path = Paths.get(pathStr); + if (PathUtil.exists(path, false)) { + PathUtil.mkParentDirs(path); + } + } + + /** + * 判断给定的请求URI是否匹配列表中的任意一个URL模式 + * 使用Ant风格的路径匹配来处理URL模式,提供了一种通配符匹配的方法 + * + * @param urls 待匹配的URL模式列表 + * @param requestURI 请求的URI + * @return 如果请求URI匹配列表中的任意一个URL模式,则返回true;否则返回false + */ + public static boolean isMatch(List urls, String requestURI) { + return urls.stream() + .anyMatch(url -> antPathMatcher.match(url, requestURI)); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/core/util/SpringContextUtils.java b/cool-admin-java/src/main/java/com/cool/core/util/SpringContextUtils.java new file mode 100644 index 0000000..f026531 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/SpringContextUtils.java @@ -0,0 +1,45 @@ +package com.cool.core.util; + +import java.util.Map; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Context 工具类 + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + + public static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + public static Object getBean(String name) { + return applicationContext.getBean(name); + } + + public static T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + public static boolean isSingleton(String name) { + return applicationContext.isSingleton(name); + } + + public static Class getType(String name) { + return applicationContext.getType(name); + } + + public static Map getBeansOfType(Class requiredType) { + return applicationContext.getBeansOfType(requiredType); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/core/util/TenantUtil.java b/cool-admin-java/src/main/java/com/cool/core/util/TenantUtil.java new file mode 100644 index 0000000..66d20e4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/core/util/TenantUtil.java @@ -0,0 +1,14 @@ +package com.cool.core.util; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +public class TenantUtil { + public static Long getTenantId() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + if (attributes == null) { + return null; + } + return (Long) attributes.getAttribute("tenantId", RequestAttributes.SCOPE_REQUEST); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCodingController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCodingController.java new file mode 100644 index 0000000..c1cce5c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCodingController.java @@ -0,0 +1,43 @@ +package com.cool.modules.base.controller.admin; + + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.request.R; +import com.cool.modules.base.dto.sys.CodeContentDto; +import com.cool.modules.base.service.sys.BaseCodingService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +/** + * ai 编码 + */ +@CoolRestController +@RequiredArgsConstructor +public class AdminBaseCodingController { + + private final BaseCodingService baseCodingService; + + @TokenIgnore + @Operation(summary = "获取模块目录结构", description = "获取模块目录结构") + @GetMapping("/getModuleTree") + public R getModuleTree() { + return R.ok(baseCodingService.getModuleTree()); + } + + @TokenIgnore + @Operation(summary = "创建代码", description = "创建代码") + @PostMapping("/createCode") + public R createCode(@RequestAttribute JSONObject requestParams) { + JSONArray codes = requestParams.get("codes", JSONArray.class); + CoolPreconditions.checkEmpty(codes); + this.baseCodingService.createCode(codes.toList(CodeContentDto.class)); + return R.ok(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCommController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCommController.java new file mode 100644 index 0000000..451ab3c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseCommController.java @@ -0,0 +1,107 @@ +package com.cool.modules.base.controller.admin; + +import cn.hutool.core.lang.Dict; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.eps.CoolEps; +import com.cool.core.file.FileUploadStrategyFactory; +import com.cool.core.request.R; +import com.cool.core.util.I18nUtil; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.service.sys.BaseSysLoginService; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.cool.modules.base.service.sys.BaseSysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +/** + * 系统通用接口, 每个人都有权限操作 + */ +@RequiredArgsConstructor +@Tag(name = "系统通用", description = "系统通用") +@CoolRestController() +public class AdminBaseCommController { + + final private BaseSysPermsService baseSysPermsService; + + final private BaseSysUserService baseSysUserService; + + final private BaseSysLoginService baseSysLoginService; + + final private CoolEps coolEps; + + final private FileUploadStrategyFactory fileUploadStrategyFactory; + + @TokenIgnore + @Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务") + @GetMapping("/eps") + public R eps() { + return R.ok(coolEps.getAdmin()); + } + + @Operation(summary = "个人信息") + @GetMapping("/person") + public R person(@RequestAttribute() Long adminUserId) { + BaseSysUserEntity baseSysUserEntity = baseSysUserService.getById(adminUserId); + baseSysUserEntity.setPassword(null); + baseSysUserEntity.setPasswordV(null); + return R.ok(baseSysUserEntity); + } + + @Operation(summary = "修改个人信息") + @PostMapping("/personUpdate") + public R personUpdate(@RequestAttribute Long adminUserId, @RequestBody Dict body) { + baseSysUserService.personUpdate(adminUserId, body); + return R.ok(); + } + + @Operation(summary = "权限与菜单") + @GetMapping("/permmenu") + public R permmenu(@RequestAttribute() Long adminUserId) { + Dict permmenu = baseSysPermsService.permmenu(adminUserId); + List list = (List) permmenu.getObj("menus"); + list.forEach(o -> o.setName(I18nUtil.getI18nMenu(o.getName()))); + return R.ok(permmenu); + } + + @Operation(summary = "文件上传") + @PostMapping(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, + MediaType.ALL_VALUE}) + public R upload( + @RequestPart(value = "file", required = false) @Parameter(description = "文件") MultipartFile[] files, + HttpServletRequest request) { + return R.ok(fileUploadStrategyFactory.upload(files, request)); + } + + @Operation(summary = "文件上传模式") + @GetMapping("/uploadMode") + public R uploadMode() { + return R.ok(fileUploadStrategyFactory.getMode()); + } + + @Operation(summary = "退出") + @PostMapping("/logout") + public R logout(@RequestAttribute Long adminUserId, @RequestAttribute String adminUsername) { + baseSysLoginService.logout(adminUserId, adminUsername); + return R.ok(); + } + + @TokenIgnore + @Operation(summary = "编程") + @GetMapping("/program") + public R program() { + return R.ok("Java"); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseOpenController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseOpenController.java new file mode 100644 index 0000000..ed87680 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/AdminBaseOpenController.java @@ -0,0 +1,121 @@ +package com.cool.modules.base.controller.admin; + +import static com.cool.core.plugin.consts.PluginConsts.captchaHook; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.eps.CoolEps; +import com.cool.core.plugin.service.CoolPluginService; +import com.cool.core.request.R; +import com.cool.core.util.CoolPluginInvokers; +import com.cool.modules.base.dto.sys.BaseSysLoginDto; +import com.cool.modules.base.service.sys.BaseSysLoginService; +import com.cool.modules.plugin.entity.PluginInfoEntity; +import io.micrometer.common.util.StringUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** + * 系统开放接口,无需权限校验 + */ +@RequiredArgsConstructor +@Tag(name = "系统开放", description = "系统开放") +@CoolRestController() +public class AdminBaseOpenController { + + final private BaseSysLoginService baseSysLoginService; + final private CoolPluginService coolPluginService; + final private CoolEps coolEps; + final private CoolCache coolCache; + + @Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务") + @GetMapping("/eps") + public R eps() { + return R.ok(coolEps.getAdmin()); + } + + @Operation(summary = "获得网页内容的参数值") + @GetMapping("/html") + public R html() { + return R.ok(); + } + + @Operation(summary = "登录") + @PostMapping("/login") + public R login(@RequestBody BaseSysLoginDto baseSysLoginDto) { + return R.ok(baseSysLoginService.login(baseSysLoginDto)); + } + + @Operation(summary = "验证码") + @GetMapping("/captcha") + public R captcha(@Parameter(description = "类型:svg|base64") @RequestParam(defaultValue = "base64") String type, + @Parameter(description = "宽度") @RequestParam(defaultValue = "150") Integer width, + @Parameter(description = "高度") @RequestParam(defaultValue = "50") Integer height) { + return R.ok(baseSysLoginService.captcha(UserTypeEnum.ADMIN, type, width, height)); + } + + @Operation(summary = "刷新token") + @GetMapping("/refreshToken") + public R refreshToken(String refreshToken) { + return R.ok(baseSysLoginService.refreshToken(refreshToken)); + } + + @RequestMapping("/gen") + @ResponseBody + public Object genCaptcha(@RequestParam(value = "type", required = false)String type) { + if (StringUtils.isBlank(type)) { + type = "SLIDER"; + } + if ("RANDOM".equals(type)) { + int i = ThreadLocalRandom.current().nextInt(0, 4); + if (i == 0) { + type = "SLIDER"; + } else if (i == 1) { + type = "CONCAT"; + } else if (i == 2) { + type = "ROTATE"; + } else{ + type = "WORD_IMAGE_CLICK"; + } + + } + return CoolPluginInvokers.invoke("tianai", "generateCaptcha", type); + } + + @PostMapping("/check") + @ResponseBody + public Object checkCaptcha(@RequestAttribute() JSONObject requestParams) { + Object result = CoolPluginInvokers.invoke("tianai", "matching", requestParams); + Map map = BeanUtil.beanToMap(result); + if (ObjUtil.equals(map.get("code"), 200)) { + String code = ThreadLocalRandom.current().nextInt(100000, 999999) + ""; + coolCache.set("verify:img:" + requestParams.getStr("id"), code, 1800); + R r = new R(); + r.put("data", Map.of("id", requestParams.getStr("id"), + "code", code)); + r.put("code", map.get("code")); + return r; + } + return result; + } + + @Operation(summary = "验证码类型") + @GetMapping("/captchaMode") + public R captchaMode() { + PluginInfoEntity pluginInfoEntity = coolPluginService.getPluginInfoEntityByHook( + captchaHook); + if (pluginInfoEntity != null) { + return R.ok(CoolPluginInvokers.invoke(pluginInfoEntity.getKey(), "getMode")); + } + return R.ok(Map.of("mode", "common")); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysDepartmentController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysDepartmentController.java new file mode 100644 index 0000000..b969055 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysDepartmentController.java @@ -0,0 +1,35 @@ +package com.cool.modules.base.controller.admin.sys; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity; +import com.cool.modules.base.service.sys.BaseSysDepartmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * 系统部门 + */ +@Tag(name = "系统部门", description = "系统部门") +@CoolRestController(api = { "add", "delete", "update", "list" }) +public class AdminBaseSysDepartmentController + extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + } + + @Operation(summary = "排序") + @PostMapping("/order") + public R order(@RequestBody List list) { + this.service.order(list); + return R.ok(); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysLogController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysLogController.java new file mode 100644 index 0000000..7b1ff35 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysLogController.java @@ -0,0 +1,57 @@ +package com.cool.modules.base.controller.admin.sys; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.base.entity.sys.BaseSysLogEntity; +import com.cool.modules.base.entity.sys.table.BaseSysLogEntityTableDef; +import com.cool.modules.base.entity.sys.table.BaseSysUserEntityTableDef; +import com.cool.modules.base.service.sys.BaseSysConfService; +import com.cool.modules.base.service.sys.BaseSysLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +/** + * 系统日志 + */ +@RequiredArgsConstructor +@Tag(name = "系统日志", description = "系统日志") +@CoolRestController(api = {"page"}) +public class AdminBaseSysLogController extends BaseController { + + private final BaseSysConfService baseSysConfService; + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption( + createOp() + .keyWordLikeFields( + BaseSysUserEntityTableDef.BASE_SYS_USER_ENTITY.NAME, + BaseSysLogEntityTableDef.BASE_SYS_LOG_ENTITY.PARAMS)); + } + + @Operation(summary = "清理日志") + @PostMapping("/clear") + public R clear() { + service.clear(true); + return R.ok(); + } + + @Operation(summary = "设置日志保存时间") + @PostMapping("/setKeep") + public R setKeep(@RequestAttribute JSONObject requestParams) { + baseSysConfService.updateValue("logKeep", requestParams.getStr("value")); + return R.ok(); + } + + @Operation(summary = "获得日志报错时间") + @PostMapping("/getKeep") + public R getKeep() { + return R.ok(baseSysConfService.getValue("logKeep")); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysMenuController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysMenuController.java new file mode 100644 index 0000000..fbe96e2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysMenuController.java @@ -0,0 +1,66 @@ +package com.cool.modules.base.controller.admin.sys; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.request.CrudOption; +import com.cool.core.request.R; +import com.cool.core.util.I18nUtil; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.service.sys.BaseSysMenuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * 系统菜单 + */ +@Tag(name = "系统菜单", description = "系统菜单") +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) +public class AdminBaseSysMenuController extends + BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + CrudOption transform = createOp() + .transform(o -> { + BaseSysMenuEntity entity = (BaseSysMenuEntity) o; + entity.setName(I18nUtil.getI18nMenu(entity.getName())); + }); + setPageOption(transform); + setListOption(transform); + setInfoOption(transform); + } + + @Operation(summary = "创建代码", description = "创建代码") + @PostMapping("/create") + public R create(@RequestBody() Map params) { + CoolPreconditions.checkEmpty(params.get("module"), "module参数不能为空"); + CoolPreconditions.checkEmpty(params.get("entity"), "entity参数不能为空"); + CoolPreconditions.checkEmpty(params.get("controller"), "controller参数不能为空"); + CoolPreconditions.checkEmpty(params.get("service"), "service参数不能为空"); + CoolPreconditions.checkEmpty(params.get("service-impl"), "service-impl参数不能为空"); + CoolPreconditions.checkEmpty(params.get("mapper"), "mapper参数不能为空"); + CoolPreconditions.checkEmpty(params.get("fileName"), "fileName参数不能为空"); + this.service.create(params); + return R.ok(); + } + + @Operation(summary = "导出", description = "导出") + @PostMapping("/export") + public R export(@RequestBody Map params) { + return R.ok(this.service.export(getIds(params))); + } + + @Operation(summary = "导入", description = "导入") + @PostMapping("/import") + public R importMenu(@RequestBody Map> params) { + CoolPreconditions.checkEmpty(params.get("menus"), "参数不能为空"); + return R.ok(this.service.importMenu(params.get("menus"))); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysParamController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysParamController.java new file mode 100644 index 0000000..56f7291 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysParamController.java @@ -0,0 +1,33 @@ +package com.cool.modules.base.controller.admin.sys; + +import static com.cool.modules.base.entity.sys.table.BaseSysParamEntityTableDef.BASE_SYS_PARAM_ENTITY; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.base.entity.sys.BaseSysParamEntity; +import com.cool.modules.base.service.sys.BaseSysParamService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * 系统参数配置 + */ +@Tag(name = "系统参数配置", description = "系统参数配置") +@CoolRestController(api = { "add", "delete", "update", "page", "info" }) +public class AdminBaseSysParamController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp().fieldEq(BASE_SYS_PARAM_ENTITY.DATA_TYPE) + .keyWordLikeFields(BASE_SYS_PARAM_ENTITY.NAME, BASE_SYS_PARAM_ENTITY.KEY_NAME)); + } + + @Operation(summary = "根据键返回网页的参数值") + @GetMapping("/html") + public String html(String key) { + return service.htmlByKey(key); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysRoleController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysRoleController.java new file mode 100644 index 0000000..51f0993 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysRoleController.java @@ -0,0 +1,37 @@ +package com.cool.modules.base.controller.admin.sys; + +import static com.cool.modules.base.entity.sys.table.BaseSysRoleEntityTableDef.BASE_SYS_ROLE_ENTITY; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.base.entity.sys.BaseSysRoleEntity; +import com.cool.modules.base.service.sys.BaseSysRoleService; +import com.mybatisflex.core.query.QueryWrapper; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; + +/** + * 系统角色 + */ +@Tag(name = "系统角色", description = "系统角色") +@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) +public class AdminBaseSysRoleController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + JSONObject tokenInfo = requestParams.getJSONObject("tokenInfo"); + boolean isAdmin = tokenInfo.getStr("username").equals("admin"); + + setPageOption(createOp().keyWordLikeFields(BASE_SYS_ROLE_ENTITY.NAME, BASE_SYS_ROLE_ENTITY.LABEL).queryWrapper(QueryWrapper.create().and(qw -> { + qw.eq(BASE_SYS_ROLE_ENTITY.USER_ID.getName(), tokenInfo.getLong("userId")).or(w -> { + Object o = tokenInfo.get("roleIds"); + if (o != null) { + w.in(BASE_SYS_ROLE_ENTITY.ID.getName(), new JSONArray(o).toList(Long.class)); + } + }); + }, !isAdmin).and(BASE_SYS_ROLE_ENTITY.LABEL.ne("admin")))); + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysUserController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysUserController.java new file mode 100644 index 0000000..aa15e18 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/admin/sys/AdminBaseSysUserController.java @@ -0,0 +1,34 @@ +package com.cool.modules.base.controller.admin.sys; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.service.sys.BaseSysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 系统用户 + */ +@Tag(name = "系统用户", description = "系统用户") +@CoolRestController(api = { "add", "delete", "update", "page", "info" }) +public class AdminBaseSysUserController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + } + + @Operation(summary = "移动部门") + @PostMapping("/move") + public R move(@RequestAttribute JSONObject requestParams) { + service.move(requestParams.getLong("departmentId"), requestParams.get("userIds", Long[].class)); + return R.ok(); + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/controller/app/AppBaseCommController.java b/cool-admin-java/src/main/java/com/cool/modules/base/controller/app/AppBaseCommController.java new file mode 100644 index 0000000..d326b0a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/controller/app/AppBaseCommController.java @@ -0,0 +1,71 @@ +package com.cool.modules.base.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.eps.CoolEps; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.file.FileUploadStrategyFactory; +import com.cool.core.request.R; +import com.cool.modules.base.service.sys.BaseSysParamService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +/** + * app通用接口 + */ +@RequiredArgsConstructor +@Tag(name = "应用通用", description = "应用通用") +@CoolRestController +public class AppBaseCommController { + + private final CoolEps coolEps; + + private final BaseSysParamService baseSysParamService; + + @Value("${cool.sysParam.allowKeys:[]}") + private List allowKeys; + + final private FileUploadStrategyFactory fileUploadStrategyFactory; + + @TokenIgnore + @Operation(summary = "参数配置") + @GetMapping("/param") + public R param(@RequestAttribute() JSONObject requestParams) { + String key = requestParams.get("key", String.class); + CoolPreconditions.check(!allowKeys.contains(key), "非法操作"); + return R.ok(baseSysParamService.dataByKey(key)); + } + + @TokenIgnore + @Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务") + @GetMapping("/eps") + public R eps() { + return R.ok(coolEps.getApp()); + } + + + @Operation(summary = "文件上传") + @PostMapping(value = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.ALL_VALUE }) + public R upload(@RequestPart(value = "file", required = false) @Parameter(description = "文件") MultipartFile[] files, + HttpServletRequest request) { + return R.ok(fileUploadStrategyFactory.upload(files, request)); + } + + @Operation(summary = "文件上传模式") + @GetMapping("/uploadMode") + public R uploadMode() { + return R.ok(fileUploadStrategyFactory.getMode()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/BaseSysLoginDto.java b/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/BaseSysLoginDto.java new file mode 100644 index 0000000..20f3149 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/BaseSysLoginDto.java @@ -0,0 +1,30 @@ +package com.cool.modules.base.dto.sys; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * 登录 + */ +@Data +@Schema(description = "登录参数") +public class BaseSysLoginDto { + + @Schema(description = "用户名") + @NotBlank + private String username; + + @Schema(description = "密码") + @NotBlank + private String password; + + @Schema(description = "验证码ID") + @NotBlank + private String captchaId; + + @Schema(description = "验证码") + @NotBlank + private String verifyCode; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/CodeContentDto.java b/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/CodeContentDto.java new file mode 100644 index 0000000..bcff941 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/dto/sys/CodeContentDto.java @@ -0,0 +1,9 @@ +package com.cool.modules.base.dto.sys; + +import lombok.Data; + +@Data +public class CodeContentDto { + private String path; + private String content; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysConfEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysConfEntity.java new file mode 100644 index 0000000..73ffbb1 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysConfEntity.java @@ -0,0 +1,24 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; +import org.dromara.autotable.annotation.enums.IndexTypeEnum; + +/** + * 系统配置 + */ +@Getter +@Setter +@Table(value = "base_sys_conf", comment = "系统配置表") +public class BaseSysConfEntity extends BaseEntity { + @Index(type = IndexTypeEnum.UNIQUE) + @ColumnDefine(comment = "配置键", notNull = true) + private String cKey; + + @ColumnDefine(comment = "值", notNull = true, type = "text") + private String cValue; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysDepartmentEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysDepartmentEntity.java new file mode 100644 index 0000000..200a126 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysDepartmentEntity.java @@ -0,0 +1,30 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; + +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.mybatisflex.annotation.Table; +import lombok.Getter; +import lombok.Setter; + +/** + * 系统部门 + */ +@Getter +@Setter +@Table(value = "base_sys_department", comment = "系统部门") +public class BaseSysDepartmentEntity extends BaseEntity { + @ColumnDefine(comment = "部门名称", notNull = true) + private String name; + + @ColumnDefine(comment = "上级部门ID", type = "bigint") + private Long parentId; + + @ColumnDefine(comment = "排序", defaultValue = "0") + private Integer orderNum; + + // 父菜单名称 + @Column(ignore = true) + private String parentName; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysLogEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysLogEntity.java new file mode 100644 index 0000000..450960c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysLogEntity.java @@ -0,0 +1,34 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.cool.core.mybatis.handler.Fastjson2TypeHandler; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "base_sys_log", comment = "系统日志表") +public class BaseSysLogEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "用户ID", type = "bigint") + private Long userId; + + @ColumnDefine(comment = "行为", length = 1000) + private String action; + + @ColumnDefine(comment = "IP", length = 50) + private String ip; + + @ColumnDefine(comment = "参数", type = "json") + @Column(typeHandler = Fastjson2TypeHandler.class) + private Object params; + + // 用户名称 + @Column(ignore = true) + private String name; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysMenuEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysMenuEntity.java new file mode 100644 index 0000000..96c4ca4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysMenuEntity.java @@ -0,0 +1,52 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "base_sys_menu", comment = "系统菜单表") +public class BaseSysMenuEntity extends BaseEntity { + @Index + @ColumnDefine(comment = "父菜单ID", type = "bigint") + private Long parentId; + + @ColumnDefine(comment = "菜单名称") + private String name; + + @ColumnDefine(comment = "权限", type = "text") + private String perms; + + @ColumnDefine(comment = "类型 0:目录 1:菜单 2:按钮", defaultValue = "0") + private Integer type; + + @ColumnDefine(comment = "图标") + private String icon; + + @ColumnDefine(comment = "排序", defaultValue = "0") + private Integer orderNum; + + @ColumnDefine(comment = "菜单地址") + private String router; + + @ColumnDefine(comment = "视图地址") + private String viewPath; + + @ColumnDefine(comment = "路由缓存", defaultValue = "true") + private Boolean keepAlive; + + @ColumnDefine(comment = "是否显示", defaultValue = "true") + private Boolean isShow; + + @Column(ignore = true) + private String parentName; + + @Column(ignore = true) + private List childMenus; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysParamEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysParamEntity.java new file mode 100644 index 0000000..d5f1001 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysParamEntity.java @@ -0,0 +1,29 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "base_sys_param", comment = "系统参数配置") +public class BaseSysParamEntity extends BaseEntity { + @Index + @ColumnDefine(comment = "键", notNull = true) + private String keyName; + + @ColumnDefine(comment = "名称") + private String name; + + @ColumnDefine(comment = "数据", type = "text") + private String data; + + @ColumnDefine(comment = "数据类型 0:字符串 1:数组 2:键值对", defaultValue = "0") + private Integer dataType; + + @ColumnDefine(comment = "备注") + private String remark; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleDepartmentEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleDepartmentEntity.java new file mode 100644 index 0000000..f7a48ff --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleDepartmentEntity.java @@ -0,0 +1,20 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; + +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.mybatisflex.annotation.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "base_sys_role_department", comment = "系统角色部门") +public class BaseSysRoleDepartmentEntity extends BaseEntity { + + @ColumnDefine(comment = "角色ID", type = "bigint") + private Long roleId; + + @ColumnDefine(comment = "部门ID", type = "bigint") + private Long departmentId; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleEntity.java new file mode 100644 index 0000000..8102e2d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleEntity.java @@ -0,0 +1,43 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.cool.core.mybatis.handler.Fastjson2TypeHandler; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; +import org.dromara.autotable.annotation.enums.IndexTypeEnum; + +@Getter +@Setter +@Table(value = "base_sys_role", comment = "系统角色表") +public class BaseSysRoleEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "用户ID", notNull = true, type = "bigint") + private Long userId; + + @ColumnDefine(comment = "名称", notNull = true) + private String name; + + @Index(type = IndexTypeEnum.UNIQUE) + @ColumnDefine(comment = "角色标签", notNull = true) + private String label; + + @ColumnDefine(comment = "备注") + private String remark; + + @ColumnDefine(comment = "数据权限是否关联上下级", defaultValue = "1") + private Integer relevance; + + @ColumnDefine(comment = "菜单权限", type = "json") + @Column(typeHandler = Fastjson2TypeHandler.class) + private List menuIdList; + + @ColumnDefine(comment = "部门权限", type = "json") + @Column(typeHandler = Fastjson2TypeHandler.class) + private List departmentIdList; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleMenuEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleMenuEntity.java new file mode 100644 index 0000000..0886904 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysRoleMenuEntity.java @@ -0,0 +1,19 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; + +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.mybatisflex.annotation.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "base_sys_role_menu", comment = "系统角色菜单表") +public class BaseSysRoleMenuEntity extends BaseEntity { + @ColumnDefine(comment = "菜单", type = "bigint") + private Long menuId; + + @ColumnDefine(comment = "角色ID", type = "bigint") + private Long roleId; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserEntity.java new file mode 100644 index 0000000..fa5b5c0 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserEntity.java @@ -0,0 +1,72 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.TenantEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Ignore; +import org.dromara.autotable.annotation.Index; +import org.dromara.autotable.annotation.enums.IndexTypeEnum; + +import java.util.List; + +@Getter +@Setter +@Table(value = "base_sys_user", comment = "系统用户表") +public class BaseSysUserEntity extends TenantEntity { + @Index + @ColumnDefine(comment = "部门ID", type = "bigint") + private Long departmentId; + + @ColumnDefine(comment = "姓名") + private String name; + + @Index(type = IndexTypeEnum.UNIQUE) + @ColumnDefine(comment = "用户名", length = 100, notNull = true) + private String username; + + @ColumnDefine(comment = "密码", notNull = true) + private String password; + + @ColumnDefine(comment = "密码版本", defaultValue = "1") + private Integer passwordV; + + @ColumnDefine(comment = "昵称", notNull = true) + private String nickName; + + @ColumnDefine(comment = "头像") + private String headImg; + + @ColumnDefine(comment = "手机号") + private String phone; + + @ColumnDefine(comment = "邮箱") + private String email; + + @ColumnDefine(comment = "备注") + private String remark; + + @ColumnDefine(comment = "状态 0:禁用 1:启用", defaultValue = "1") + private Integer status; + + // 部门名称 + @Column(ignore = true) + private String departmentName; + + // 角色名称 + @Column(ignore = true) + private String roleName; + + @ColumnDefine(comment = "socketId") + private String socketId; + + + @Ignore + @Schema( description = "角色列表" ) + private List roleIdList; + + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserRoleEntity.java b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserRoleEntity.java new file mode 100644 index 0000000..c8ffc67 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/entity/sys/BaseSysUserRoleEntity.java @@ -0,0 +1,21 @@ +package com.cool.modules.base.entity.sys; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "base_sys_user_role", comment = "系统用户角色表") +public class BaseSysUserRoleEntity extends BaseEntity { + @Index + @ColumnDefine(comment = "用户ID", type = "bigint") + private Long userId; + + @Index + @ColumnDefine(comment = "角色ID", type = "bigint") + private Long roleId; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/filter/BaseLogFilter.java b/cool-admin-java/src/main/java/com/cool/modules/base/filter/BaseLogFilter.java new file mode 100644 index 0000000..9e10474 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/filter/BaseLogFilter.java @@ -0,0 +1,33 @@ +package com.cool.modules.base.filter; + +import cn.hutool.json.JSONObject; +import com.cool.modules.base.service.sys.BaseSysLogService; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Component +@Order(10) +@RequiredArgsConstructor +public class BaseLogFilter implements Filter { + + final private BaseSysLogService baseSysLogService; + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, + FilterChain filterChain) + throws IOException, ServletException { + // 记录日志 + HttpServletRequest request = (HttpServletRequest) servletRequest; + baseSysLogService.record(request, (JSONObject) request.getAttribute("requestParams")); + filterChain.doFilter(servletRequest, servletResponse); + } + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysConfMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysConfMapper.java new file mode 100644 index 0000000..298448c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysConfMapper.java @@ -0,0 +1,11 @@ +package com.cool.modules.base.mapper.sys; + +import com.cool.modules.base.entity.sys.BaseSysConfEntity; +import com.mybatisflex.core.BaseMapper; + +/** + * 系统配置 + */ +public interface BaseSysConfMapper extends BaseMapper { + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysDepartmentMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysDepartmentMapper.java new file mode 100644 index 0000000..cc3148e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysDepartmentMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity; + +/** + * 系统部门 + */ +public interface BaseSysDepartmentMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysLogMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysLogMapper.java new file mode 100644 index 0000000..5bd6f09 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysLogMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysLogEntity; + +/** + * 系统日志 + */ +public interface BaseSysLogMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysMenuMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysMenuMapper.java new file mode 100644 index 0000000..ab39f68 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysMenuMapper.java @@ -0,0 +1,20 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 系统菜单 + */ +public interface BaseSysMenuMapper extends BaseMapper { + /** + * 根据角色ID获得所有菜单 + * + * @param roleIds 角色ID集合 + * @return SysMenuEntity + */ + List getMenus(@Param("roleIds") Long[] roleIds); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysParamMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysParamMapper.java new file mode 100644 index 0000000..49ce1bd --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysParamMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysParamEntity; + +/** + * 系统参数配置 + */ +public interface BaseSysParamMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleDepartmentMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleDepartmentMapper.java new file mode 100644 index 0000000..9f3f027 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleDepartmentMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysRoleDepartmentEntity; + +/** + * 系统角色部门 + */ +public interface BaseSysRoleDepartmentMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMapper.java new file mode 100644 index 0000000..6264504 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysRoleEntity; + +/** + * 系统角色 + */ +public interface BaseSysRoleMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMenuMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMenuMapper.java new file mode 100644 index 0000000..4d488f0 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysRoleMenuMapper.java @@ -0,0 +1,18 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysRoleMenuEntity; +import org.apache.ibatis.annotations.Param; + +/** + * 系统角色菜单 + */ +public interface BaseSysRoleMenuMapper extends BaseMapper { + /** + * 跟菜单关联的所有用户 + * + * @param menuId 菜单 + * @return 所有用户ID + */ + Long[] userIds(@Param("menuId") Long menuId); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserMapper.java new file mode 100644 index 0000000..37f4c90 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserMapper.java @@ -0,0 +1,11 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; + +/** + * 系统用户 + */ +public interface BaseSysUserMapper extends BaseMapper { + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserRoleMapper.java b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserRoleMapper.java new file mode 100644 index 0000000..51b8105 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/mapper/sys/BaseSysUserRoleMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.mapper.sys; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.base.entity.sys.BaseSysUserRoleEntity; + +/** + * 系统用户角色 + */ +public interface BaseSysUserRoleMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/security/JwtUserDetailsServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/security/JwtUserDetailsServiceImpl.java new file mode 100644 index 0000000..574dce7 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/security/JwtUserDetailsServiceImpl.java @@ -0,0 +1,57 @@ +package com.cool.modules.base.security; + +import cn.hutool.core.util.ObjectUtil; +import com.cool.core.cache.CoolCache; +import com.cool.core.security.jwt.JwtUser; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.cool.modules.base.service.sys.BaseSysUserService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +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.Component; + +/** + * 获得用户信息 + */ +@Component +@RequiredArgsConstructor +public class JwtUserDetailsServiceImpl implements UserDetailsService { + + final private BaseSysUserService baseSysUserService; + final private BaseSysPermsService baseSysPermsService; + final private CoolCache coolCache; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + BaseSysUserEntity sysUserEntity = baseSysUserService.getMapper().selectOneByQuery( + QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username) + .eq(BaseSysUserEntity::getStatus, 1)); + if (ObjectUtil.isEmpty(sysUserEntity)) { + throw new UsernameNotFoundException("用户名不存在"); + } + List authority = new ArrayList<>(); + String[] perms = baseSysPermsService.getPerms(sysUserEntity.getId()); + for (String perm : perms) { + authority.add(new SimpleGrantedAuthority(perm)); + } + Long[] departmentIds = baseSysPermsService.getDepartmentIdsByRoleIds(sysUserEntity.getId()); + JwtUser jwtUser = new JwtUser(sysUserEntity.getId(), sysUserEntity.getUsername(), sysUserEntity.getPassword(), + authority, + sysUserEntity.getStatus() == 1); + Long[] roleIds = baseSysPermsService.getRoles(sysUserEntity); + coolCache.set("admin:userDetails:" + jwtUser.getUsername(), jwtUser); + coolCache.set("admin:passwordVersion:" + sysUserEntity.getId(), + sysUserEntity.getPasswordV()); + coolCache.set("admin:userInfo:" + sysUserEntity.getId(), sysUserEntity); + coolCache.set("admin:department:" + sysUserEntity.getId(), departmentIds); + coolCache.set("admin:roleIds:" + sysUserEntity.getId(), roleIds); + return jwtUser; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/security/MySecurityMetadataSource.java b/cool-admin-java/src/main/java/com/cool/modules/base/security/MySecurityMetadataSource.java new file mode 100644 index 0000000..2f97be4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/security/MySecurityMetadataSource.java @@ -0,0 +1,81 @@ +package com.cool.modules.base.security; + +import cn.hutool.core.util.ObjectUtil; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.access.SecurityConfig; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; +import org.springframework.stereotype.Component; + +/** + * 权限资源管理器 为权限决断器提供支持 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource { + + final private BaseSysPermsService baseSysPermsService; + + private Map> map = null; + + /** + * 加载权限表中所有操作请求权限 + */ + public void loadResourceDefine() { + map = new HashMap<>(); + Collection configAttributes; + ConfigAttribute cfg; + String[] perms = baseSysPermsService.getAllPerms(); + // 获取启用的权限操作请求 + for (String perm : perms) { + configAttributes = new ArrayList<>(); + cfg = new SecurityConfig(perm); + // 作为MyAccessDecisionManager类的decide的第三个参数 + configAttributes.add(cfg); + // 用权限的path作为map的key,用ConfigAttribute的集合作为value + map.put(perm.replaceAll(":", "/"), configAttributes); + } + } + + /** + * 判定用户请求的url是否在权限表中 如果在权限表中,则返回给decide方法,用来判定用户是否有此权限 如果不在权限表中则放行 + * + * @param o + * @return + * @throws IllegalArgumentException + */ + @Override + public Collection getAttributes(Object o) throws IllegalArgumentException { + UserTypeEnum userTypeEnum = CoolSecurityUtil.getCurrentUserType(); + if (ObjectUtil.equal(userTypeEnum, UserTypeEnum.APP)) { + // app用户不需要权限拦截 + return null; + } + if (map == null) { + loadResourceDefine(); + } + // Object中包含用户请求request + String url = ((FilterInvocation) o).getRequestUrl(); + return map.get(url.replace("/admin/", "").split("[?]")[0]); + } + + @Override + public Collection getAllConfigAttributes() { + return new ArrayList<>(); + } + + @Override + public boolean supports(Class aClass) { + return true; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseCodingService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseCodingService.java new file mode 100644 index 0000000..303fb62 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseCodingService.java @@ -0,0 +1,11 @@ +package com.cool.modules.base.service.sys; + +import com.cool.modules.base.dto.sys.CodeContentDto; + +import java.util.List; + +public interface BaseCodingService { + List getModuleTree(); + + void createCode(List codes); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysConfService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysConfService.java new file mode 100644 index 0000000..3117a6f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysConfService.java @@ -0,0 +1,41 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysConfEntity; + +/** + * 系统配置 + */ +public interface BaseSysConfService extends BaseService { + /** + * 更新配置 + * + * @param key 键 + * @param value 值 + */ + void updateValue(String key, String value); + + /** + * 获得值 + * + * @param key 键 + * @return 值 + */ + String getValue(String key); + + /** + * 获得值(带缓存) + * + * @param key 键 + * @return 值 + */ + String getValueWithCache(String key); + + /** + * 设置值 + * + * @param key 键 + * @param value 值 + */ + void setValue(String key, String value); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysDepartmentService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysDepartmentService.java new file mode 100644 index 0000000..502862d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysDepartmentService.java @@ -0,0 +1,18 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity; + +import java.util.List; + +/** + * 系统部门 + */ +public interface BaseSysDepartmentService extends BaseService { + /** + * 排序 + * + * @param list 新的排序 + */ + void order(List list); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLogService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLogService.java new file mode 100644 index 0000000..4ca596d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLogService.java @@ -0,0 +1,27 @@ +package com.cool.modules.base.service.sys; + +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysLogEntity; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 系统日志 + */ +public interface BaseSysLogService extends BaseService { + /** + * 清理日志 + * + * @param isAll 是否全部清除 + */ + void clear(boolean isAll); + + /** + * 日志记录 + * + * @param requestParams 请求参数 + * @param request 请求 + */ + void record(HttpServletRequest request, JSONObject requestParams); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLoginService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLoginService.java new file mode 100644 index 0000000..242d8f2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysLoginService.java @@ -0,0 +1,48 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.enums.UserTypeEnum; +import com.cool.modules.base.dto.sys.BaseSysLoginDto; + +/** + * 系统登录 + */ +public interface BaseSysLoginService { + /** + * 验证码 + * + * @param type 类型 svg base64 svg是node版本的, java版本用base64, svg未实现 + * @param width 宽度 + * @param height 高度 + * @return base64 验证码与ID + */ + Object captcha(UserTypeEnum userTypeEnum, String type, Integer width, Integer height); + + /** + * 校验验证码 + */ + void captchaCheck(String captchaId, String code); + + /** + * 登录 + * + * @param baseSysLoginDto 登录必要信息 + * @return token与相关的过期信息 + */ + Object login(BaseSysLoginDto baseSysLoginDto); + + /** + * 退出登录 + * + * @param adminUserId 用户ID + * @param username 用户名称 + */ + void logout(Long adminUserId, String username); + + /** + * 刷新token + * + * @param refreshToken 刷新token + * @return 新的token + */ + Object refreshToken(String refreshToken); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysMenuService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysMenuService.java new file mode 100644 index 0000000..e7753d9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysMenuService.java @@ -0,0 +1,18 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import java.util.List; +import java.util.Map; + +/** + * 系统菜单 + */ +public interface BaseSysMenuService extends BaseService { + + Object export(List ids); + + boolean importMenu(List menus); + + void create(Map params); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysParamService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysParamService.java new file mode 100644 index 0000000..7926140 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysParamService.java @@ -0,0 +1,25 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysParamEntity; + +/** + * 系统参数配置 + */ +public interface BaseSysParamService extends BaseService { + /** + * 根据key获得网页内容 + * + * @param key 键 + * @return 网页内容 + */ + String htmlByKey(String key); + + /** + * 根据key获得数据 + * + * @param key 键 + * @return 数据 + */ + String dataByKey(String key); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysPermsService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysPermsService.java new file mode 100644 index 0000000..0e364db --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysPermsService.java @@ -0,0 +1,160 @@ +package com.cool.modules.base.service.sys; + +import cn.hutool.core.lang.Dict; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import java.util.List; + +/** + * 权限菜单 + */ +public interface BaseSysPermsService { + /** + * 获得权限缓存 + * + * @param userId 用户ID + * @return 返回用户相关的权限信息 + */ + String[] getPermsCache(Long userId); + + /** + * 获得权限 + * + * @param userId 用户ID + * @return 返回用户相关的权限信息 + */ + String[] getPerms(Long userId); + + /** + * 获得权限 + * + * @param roleIds 用户角色数组 + * @return 返回用户相关的权限信息 + */ + String[] getPerms(Long[] roleIds); + + /** + * 获得菜单 + * + * @param roleIds 角色 + * @return 返回菜单 + */ + List getMenus(Long[] roleIds); + + /** + * 获得菜单 + * + * @param userId 用户ID + * @return 返回菜单 + */ + List getMenus(Long userId); + + /** + * 获得菜单 + * + * @param username 用户名 + * @return 返回菜单 + */ + List getMenus(String username); + + /** + * 获得角色数组 + * + * @param userId 用户ID + * @return 返回角色数组 + */ + Long[] getRoles(Long userId); + + /** + * 获得角色数组 + * + * @param username 用户名 + * @return 返回角色数组 + */ + Long[] getRoles(String username); + + /** + * 获得登录用户的部门权限 + * + * @return 部门ID集合 + */ + Long[] loginDepartmentIds(); + + /** + * 根据角色获得部门ID + * + * @param roleIds 角色ID数组 + * @return 部门ID数组 + */ + Long[] getDepartmentIdsByRoleIds(Long[] roleIds); + + /** + * 根据用户ID获得部门ID + * + * @param userId 角色ID数组 + * @return 部门ID数组 + */ + Long[] getDepartmentIdsByRoleIds(Long userId); + + /** + * 获得角色数组 + * + * @param userEntity 用户 + * @return 返回角色数组 + */ + Long[] getRoles(BaseSysUserEntity userEntity); + + /** + * 所有的操作权限 + * + * @return 返回所有的操作权限 + */ + String[] getAllPerms(); + + /** + * 用户的权限菜单 + * + * @param adminUserId 登录的用户 + * @return 权限菜单 + */ + Dict permmenu(Long adminUserId); + + /** + * 更新角色权限 + * + * @param roleId 角色ID + * @param menuIdList 菜单ID + * @param departmentIds 部门ID + */ + void updatePerms(Long roleId, Long[] menuIdList, Long[] departmentIds); + + /** + * 更新用户角色 + * + * @param userId 用户ID + * @param roleIdList 角色集合 + */ + void updateUserRole(Long userId, Long[] roleIdList); + + /** + * 刷新权限 + * + * @param userId 用户ID + */ + void refreshPerms(Long userId); + + /** + * 刷新权限 + * + * @param menuId 用户ID + */ + void refreshPermsByMenuId(Long menuId); + + /** + * 刷新权限 + * + * @param roleId 角色ID + */ + void refreshPermsByRoleId(Long roleId); + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysRoleService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysRoleService.java new file mode 100644 index 0000000..f9e4546 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysRoleService.java @@ -0,0 +1,10 @@ +package com.cool.modules.base.service.sys; + +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysRoleEntity; + +/** + * 系统角色 + */ +public interface BaseSysRoleService extends BaseService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysUserService.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysUserService.java new file mode 100644 index 0000000..0a6197a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/BaseSysUserService.java @@ -0,0 +1,25 @@ +package com.cool.modules.base.service.sys; + +import cn.hutool.core.lang.Dict; +import com.cool.core.base.BaseService; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; + +/** + * 系统用户 + */ +public interface BaseSysUserService extends BaseService { + /** + * 修改用户信息 + * + * @param body 用户信息 + */ + void personUpdate(Long userId, Dict body); + + /** + * 移动部门 + * + * @param departmentId 部门ID + * @param userIds 用户ID集合 + */ + void move(Long departmentId, Long[] userIds); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseCodingServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseCodingServiceImpl.java new file mode 100644 index 0000000..627b35f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseCodingServiceImpl.java @@ -0,0 +1,83 @@ +package com.cool.modules.base.service.sys.impl; + +import com.cool.core.exception.CoolPreconditions; +import com.cool.modules.base.dto.sys.CodeContentDto; +import com.cool.modules.base.service.sys.BaseCodingService; +import com.google.common.collect.Lists; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class BaseCodingServiceImpl implements BaseCodingService { + + @Value("${spring.profiles.active}") + private String env; + + // 获取模块目录结构 + public List getModuleTree() { + if (!"local".equals(env)) { + return List.of(); // 返回空列表 + } + + // 获取基础目录 + Path modulesPath = getModulesPath(); + // 获取模块文件夹 + try { + return Files.list(modulesPath) + .filter(path -> !path.getFileName().toString().equals(".DS_Store")) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static Path getModulesPath() { + String moduleDir = System.getProperty("user.dir"); // 可通过其他方式获取应用目录 + String packageName = BaseCodingServiceImpl.class.getPackageName(); + String modulesParentPath = packageName.split("modules")[0].replace(".", File.separator); + return Paths.get(moduleDir, "src", "main", "java", modulesParentPath, "modules"); + } + + // 创建代码文件 + public void createCode(List codes) { + if (!"local".equals(env)) { + throw new IllegalArgumentException("只能在开发环境下创建代码"); + } + Path modulesPath = getModulesPath(); + String absolutePathStr = modulesPath.toAbsolutePath().toString(); + List list = Lists.newArrayList(); + try { + for (CodeContentDto code : codes) { + // 格式化代码内容 + String formattedContent = code.getContent(); + Path filePath = Paths.get(absolutePathStr, code.getPath().replace("java/", "/")); + Path dirPath = filePath.getParent(); + // 确保目录存在 + if (!Files.exists(dirPath)) { + Files.createDirectories(dirPath); + } + // 写入文件 + try (FileWriter writer = new FileWriter(filePath.toFile())) { + formattedContent = formattedContent.replace("com.tangzc.mybatisflex.autotable.annotation.Index;", + "org.dromara.autotable.annotation.Index;"); + writer.write(formattedContent); + } + list.add(filePath.toString()); + } + } catch (Exception e) { + CoolPreconditions.alwaysThrow("生成代码失败", e); + } + log.info("代码已生成,请先编译后,手动重启服务"); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysConfServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysConfServiceImpl.java new file mode 100644 index 0000000..db130a9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysConfServiceImpl.java @@ -0,0 +1,58 @@ +package com.cool.modules.base.service.sys.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.cache.CoolCache; +import com.cool.modules.base.entity.sys.BaseSysConfEntity; +import com.cool.modules.base.mapper.sys.BaseSysConfMapper; +import com.cool.modules.base.service.sys.BaseSysConfService; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统配置 + */ +@Service +@RequiredArgsConstructor +public class BaseSysConfServiceImpl extends BaseServiceImpl + implements BaseSysConfService { + + private final CoolCache coolCache; + + @Override + public void updateValue(String key, String value) { + UpdateChain.of(BaseSysConfEntity.class).set(BaseSysConfEntity::getCValue, value) + .eq(BaseSysConfEntity::getCKey, key).update(); + } + + @Override + public String getValue(String key) { + BaseSysConfEntity baseSysConfEntity = getOne(QueryWrapper.create().eq(BaseSysConfEntity::getCKey, key)); + if (baseSysConfEntity != null) { + return baseSysConfEntity.getCValue(); + } + return null; + } + + @Override + public String getValueWithCache(String key) { + String value = coolCache.get(key, String.class); + if (value != null) { + return value; + } + value = getValue(key); + if (value != null) { + coolCache.set(key, value); + } + return value; + } + + @Override + public void setValue(String key, String value) { + BaseSysConfEntity baseSysConfEntity = new BaseSysConfEntity(); + baseSysConfEntity.setCKey(key); + baseSysConfEntity.setCValue(value); + save(baseSysConfEntity); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysDepartmentServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysDepartmentServiceImpl.java new file mode 100644 index 0000000..2c284cd --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysDepartmentServiceImpl.java @@ -0,0 +1,84 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.mapper.sys.BaseSysDepartmentMapper; +import com.cool.modules.base.mapper.sys.BaseSysUserMapper; +import com.cool.modules.base.service.sys.BaseSysDepartmentService; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统部门 + */ +@RequiredArgsConstructor +@Service +public class BaseSysDepartmentServiceImpl extends + BaseServiceImpl + implements BaseSysDepartmentService { + + final private BaseSysUserMapper baseSysUserMapper; + + final private BaseSysPermsService baseSysPermsService; + + @Override + public void order(List list) { + list.forEach(baseSysDepartmentEntity -> { + UpdateChain.of(BaseSysDepartmentEntity.class) + .set(BaseSysDepartmentEntity::getOrderNum, baseSysDepartmentEntity.getOrderNum()) + .set(BaseSysDepartmentEntity::getParentId, baseSysDepartmentEntity.getParentId()) + .eq(BaseSysDepartmentEntity::getId, baseSysDepartmentEntity.getId()).update(); + }); + } + + @Override + public List list(JSONObject requestParams, QueryWrapper queryWrapper) { + String username = CoolSecurityUtil.getAdminUsername(); + Long[] loginDepartmentIds = baseSysPermsService.loginDepartmentIds(); + if (loginDepartmentIds != null && loginDepartmentIds.length == 0) { + return new ArrayList<>(); + } + List list = this.list( + QueryWrapper.create() + .in(BaseSysDepartmentEntity::getId, loginDepartmentIds, !username.equals("admin")) + .orderBy(BaseSysDepartmentEntity::getOrderNum, false)); + list.forEach(e -> { + List parentDepartment = list.stream() + .filter(sysDepartmentEntity -> e.getParentId() != null + && e.getParentId().equals(sysDepartmentEntity.getId())) + .toList(); + if (!parentDepartment.isEmpty()) { + e.setParentName(parentDepartment.get(0).getName()); + } + }); + return list; + } + + @Override + public boolean delete(JSONObject requestParams, Long... ids) { + super.delete(ids); + // 是否删除对应用户 否则移动到顶层部门 + if (requestParams.getBool("deleteUser")) { + return baseSysUserMapper + .deleteByQuery( + QueryWrapper.create().in(BaseSysUserEntity::getDepartmentId, (Object) ids)) > 0; + } else { + BaseSysDepartmentEntity topDepartment = getOne( + QueryWrapper.create().isNull(BaseSysDepartmentEntity::getParentId)); + if (topDepartment != null) { + UpdateChain.of(BaseSysUserEntity.class) + .set(BaseSysUserEntity::getDepartmentId, topDepartment.getId()) + .in(BaseSysUserEntity::getDepartmentId, (Object) ids).update(); + } + } + return false; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLogServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLogServiceImpl.java new file mode 100644 index 0000000..fd4886c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLogServiceImpl.java @@ -0,0 +1,115 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.config.LogProperties; +import com.cool.core.security.IgnoredUrlsProperties; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.core.util.IPUtils; +import com.cool.core.util.PathUtils; +import com.cool.modules.base.entity.sys.BaseSysLogEntity; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.entity.sys.table.BaseSysLogEntityTableDef; +import com.cool.modules.base.entity.sys.table.BaseSysUserEntityTableDef; +import com.cool.modules.base.mapper.sys.BaseSysLogMapper; +import com.cool.modules.base.service.sys.BaseSysConfService; +import com.cool.modules.base.service.sys.BaseSysLogService; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.concurrent.Executor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统日志 + */ +@RequiredArgsConstructor +@Service +public class BaseSysLogServiceImpl extends BaseServiceImpl + implements BaseSysLogService { + + private final BaseSysConfService baseSysConfService; + + private final IgnoredUrlsProperties ignoredUrlsProperties; + + private final IPUtils ipUtils; + + private final LogProperties logProperties; + + private final Executor logTaskExecutor; + + @Override + public Object page( + JSONObject requestParams, Page page, QueryWrapper queryWrapper) { + queryWrapper + .select( + BaseSysLogEntityTableDef.BASE_SYS_LOG_ENTITY.ALL_COLUMNS, + BaseSysUserEntityTableDef.BASE_SYS_USER_ENTITY.NAME) + .from(BaseSysLogEntityTableDef.BASE_SYS_LOG_ENTITY) + .leftJoin(BaseSysUserEntityTableDef.BASE_SYS_USER_ENTITY) + .on(BaseSysLogEntity::getUserId, BaseSysUserEntity::getId); + return mapper.paginate(page, queryWrapper); + } + + @Override + public void clear(boolean isAll) { + if (isAll) { + this.remove(QueryWrapper.create().ge(BaseSysLogEntity::getId, 0)); + } else { + String keepDay = baseSysConfService.getValue("logKeep"); + int keepDays = Integer.parseInt(StrUtil.isNotEmpty(keepDay) ? keepDay : "30"); + Date beforeDate = DateUtil.offsetDay(new Date(), -keepDays); + this.remove(QueryWrapper.create().lt(BaseSysLogEntity::getCreateTime, beforeDate)); + } + } + + @Override + public void record(HttpServletRequest request, JSONObject requestParams) { + String requestURI = request.getRequestURI(); + if (isIgnoreUrl(requestURI)) { + // 配置了忽略记录请求日志 + return; + } + String ipAddr = ipUtils.getIpAddr(request); + // 异步记录日志 + recordAsync(ipAddr, requestURI, requestParams); + } + + private boolean isIgnoreUrl(String requestURI) { + return PathUtils.isMatch(ignoredUrlsProperties.getLogUrls(), requestURI); + } + + public void recordAsync(String ipAddr, String requestURI, JSONObject requestParams) { + logTaskExecutor.execute(() -> { + JSONObject userInfo = CoolSecurityUtil.getAdminUserInfo(requestParams); + + Long userId = null; + if (userInfo != null) { + userId = userInfo.getLong("userId"); + } + + JSONObject newJSONObject = JSONUtil.parseObj(JSONUtil.toJsonStr(requestParams)); + newJSONObject.remove("tokenInfo"); + newJSONObject.remove("refreshToken"); + newJSONObject.remove("body"); + if (newJSONObject.toString().getBytes().length > logProperties.getMaxByteLength()) { + // 超过指定 + newJSONObject.clear(); + } + BaseSysLogEntity logEntity = new BaseSysLogEntity(); + logEntity.setAction(requestURI); + logEntity.setIp(ipAddr); + if (userId != null) { + logEntity.setUserId(userId); + } + logEntity.setParams(newJSONObject); + save(logEntity); + + }); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLoginServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLoginServiceImpl.java new file mode 100644 index 0000000..635f268 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysLoginServiceImpl.java @@ -0,0 +1,137 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.GifCaptcha; +import cn.hutool.captcha.generator.RandomGenerator; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.jwt.JWT; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.security.jwt.JwtTokenUtil; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.base.dto.sys.BaseSysLoginDto; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.mapper.sys.BaseSysUserMapper; +import com.cool.modules.base.service.sys.BaseSysLoginService; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.mybatisflex.core.query.QueryWrapper; +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +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.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BaseSysLoginServiceImpl implements BaseSysLoginService { + + private final CoolCache coolCache; + + private final AuthenticationManager authenticationManager; + + private final JwtTokenUtil jwtTokenUtil; + + private final BaseSysUserMapper baseSysUserMapper; + + private final BaseSysPermsService baseSysPermsService; + + @Override + public Object captcha(UserTypeEnum userTypeEnum, String type, Integer width, Integer height) { + // 1、生成验证码 2、生成对应的ID并设置在缓存中,验证码过期时间30分钟; + Map result = new HashMap<>(); + String captchaId = StrUtil.uuid(); + result.put("captchaId", captchaId); + GifCaptcha gifCaptcha = CaptchaUtil.createGifCaptcha(width, height); + if (ObjUtil.equals(userTypeEnum, UserTypeEnum.APP)) { + gifCaptcha.setGenerator(new RandomGenerator("0123456789", 4)); + } else { + gifCaptcha.setGenerator(new RandomGenerator(4)); + } + gifCaptcha.setBackground(new Color(248, 248, 248)); + gifCaptcha.setMaxColor(60); + gifCaptcha.setMinColor(55); + result.put("data", "data:image/png;base64," + gifCaptcha.getImageBase64()); + coolCache.set("verify:img:" + captchaId, gifCaptcha.getCode(), 1800); + return result; + } + + @Override + public void captchaCheck(String captchaId, String code) { + String key = "verify:img:" + captchaId; + String verifyCode = coolCache.get(key, + String.class); + boolean flag = StrUtil.isNotEmpty(verifyCode) + && verifyCode.equalsIgnoreCase(code); + if (!flag) { + coolCache.del(key); + CoolPreconditions.alwaysThrow("验证码不正确"); + } + } + + @Override + public Object login(BaseSysLoginDto baseSysLoginDto) { + // 1、检查验证码是否正确 2、执行登录操作 + captchaCheck(baseSysLoginDto.getCaptchaId(), baseSysLoginDto.getVerifyCode()); + UsernamePasswordAuthenticationToken upToken = + new UsernamePasswordAuthenticationToken( + baseSysLoginDto.getUsername(), baseSysLoginDto.getPassword()); + Authentication authentication = authenticationManager.authenticate(upToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + // 查询用户信息并生成token + BaseSysUserEntity baseSysUserEntity = + baseSysUserMapper.selectOneByQuery( + QueryWrapper.create() + .eq(BaseSysUserEntity::getUsername, baseSysLoginDto.getUsername())); + CoolPreconditions.check( + ObjectUtil.isEmpty(baseSysUserEntity) || baseSysUserEntity.getStatus() == 0, "用户已禁用"); + Long[] roleIds = baseSysPermsService.getRoles(baseSysUserEntity); + coolCache.del("verify:img:" + baseSysLoginDto.getCaptchaId()); + return generateToken(roleIds, baseSysUserEntity, null); + } + + @Override + public void logout(Long adminUserId, String username) { + CoolSecurityUtil.adminLogout(adminUserId, username); + } + + @Override + public Object refreshToken(String refreshToken) { + CoolPreconditions.check(!jwtTokenUtil.validateRefreshToken(refreshToken), "错误的refreshToken"); + JWT jwt = jwtTokenUtil.getTokenInfo(refreshToken); + CoolPreconditions.check(jwt == null || !(Boolean) jwt.getPayload("isRefresh"), + "错误的refreshToken"); + BaseSysUserEntity baseSysUserEntity = + baseSysUserMapper.selectOneById(Convert.toLong(jwt.getPayload("userId"))); + Long[] roleIds = baseSysPermsService.getRoles(baseSysUserEntity); + return generateToken(roleIds, baseSysUserEntity, refreshToken); + } + + private Dict generateToken(Long[] roleIds, BaseSysUserEntity baseSysUserEntity, String refreshToken) { + Dict tokenInfo = + Dict.create() + .set("userType", UserTypeEnum.ADMIN.name()) + .set("roleIds", roleIds) + .set("username", baseSysUserEntity.getUsername()) + .set("userId", baseSysUserEntity.getId()) + .set("tenantId", baseSysUserEntity.getTenantId()) + .set("passwordVersion", baseSysUserEntity.getPasswordV()); + String token = jwtTokenUtil.generateToken(tokenInfo); + if (StrUtil.isEmpty(refreshToken)) { + refreshToken = jwtTokenUtil.generateRefreshToken(tokenInfo); + } + return Dict.create() + .set("token", token) + .set("expire", jwtTokenUtil.getExpire()) + .set("refreshToken", refreshToken) + .set("refreshExpire", jwtTokenUtil.getRefreshExpire()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysMenuServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysMenuServiceImpl.java new file mode 100644 index 0000000..0e3d930 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysMenuServiceImpl.java @@ -0,0 +1,180 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONObject; +import com.cool.CoolApplication; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.ModifyEnum; +import com.cool.core.eps.CoolEps; +import com.cool.core.i18n.I18nGenerator; +import com.cool.core.util.CompilerUtils; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.core.util.PathUtils; +import com.cool.modules.base.entity.sys.BaseSysMenuEntity; +import com.cool.modules.base.mapper.sys.BaseSysMenuMapper; +import com.cool.modules.base.service.sys.BaseSysMenuService; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统菜单 + */ +@Service +@RequiredArgsConstructor +public class BaseSysMenuServiceImpl extends BaseServiceImpl + implements BaseSysMenuService { + + final private BaseSysPermsService baseSysPermsService; + + final private CoolEps coolEps; + + @Override + public Object list(JSONObject requestParams, QueryWrapper queryWrapper) { + List list = baseSysPermsService.getMenus(CoolSecurityUtil.getAdminUsername()); + list.forEach(e -> { + List parent = list.stream() + .filter(sysMenuEntity -> e.getParentId() != null && e.getParentId() + .equals(sysMenuEntity.getId())) + .toList(); + if (!parent.isEmpty()) { + e.setParentName(parent.get(0).getName()); + } + }); + return list; + } + + @Override + public void modifyAfter(JSONObject requestParams, BaseSysMenuEntity sysMenuEntity, + ModifyEnum type) { + if (sysMenuEntity != null && sysMenuEntity.getId() != null) { + baseSysPermsService.refreshPermsByMenuId(requestParams.getLong("id")); + } + if (requestParams.get("ids") != null) { + Long[] ids = requestParams.get("ids", Long[].class); + for (Long id : ids) { + baseSysPermsService.refreshPermsByMenuId(id); + } + } + if (ModifyEnum.ADD.equals(type) || ModifyEnum.UPDATE.equals(type)) { + SpringUtil.getBean(I18nGenerator.class).asyncGenBaseMenu(); + } + } + + @Override + public boolean delete(Long... ids) { + super.delete(ids); + for (Long id : ids) { + this.delChildMenu(id); + } + return true; + } + + /** + * 删除子菜单 + * + * @param id 删除的菜单ID + */ + private void delChildMenu(Long id) { + List delMenu = list( + QueryWrapper.create().eq(BaseSysMenuEntity::getParentId, id)); + if (CollectionUtil.isEmpty(delMenu)) { + return; + } + Long[] ids = delMenu.stream().map(BaseSysMenuEntity::getId).toArray(Long[]::new); + if (ArrayUtil.isNotEmpty(ids)) { + delete(ids); + for (Long delId : ids) { + this.delChildMenu(delId); + } + } + } + + @Override + public Object export(List ids) { + List list = list( + QueryWrapper.create().in(BaseSysMenuEntity::getId, ids)); + List parentList = list.stream() + .filter(o -> ObjUtil.isEmpty(o.getParentId())).toList(); + Map> map = list.stream() + .filter(o -> ObjUtil.isNotEmpty(o.getParentId())) + .collect(Collectors.groupingBy(BaseSysMenuEntity::getParentId)); + parentList.forEach(o -> handler(o, map)); + return parentList; + } + + private void handler(BaseSysMenuEntity parentBaseSysMenuEntity, + Map> map) { + parentBaseSysMenuEntity.setChildMenus( + map.getOrDefault(parentBaseSysMenuEntity.getId(), new ArrayList<>())); + parentBaseSysMenuEntity.getChildMenus().forEach(o -> { + handler(o, map); + o.setId(null); + o.setParentId(null); + o.setCreateTime(null); + o.setUpdateTime(null); + }); + parentBaseSysMenuEntity.setId(null); + parentBaseSysMenuEntity.setParentId(null); + parentBaseSysMenuEntity.setCreateTime(null); + parentBaseSysMenuEntity.setUpdateTime(null); + } + + @Override + public boolean importMenu(List menus) { + menus.forEach(this::importMenu); + SpringUtil.getBean(I18nGenerator.class).asyncGenBaseMenu(); + return true; + } + + private void importMenu(BaseSysMenuEntity sysMenuEntity) { + sysMenuEntity.save(); + if (ObjUtil.isNotEmpty(sysMenuEntity.getChildMenus())) { + sysMenuEntity.getChildMenus().forEach(o -> { + o.setParentId(sysMenuEntity.getId()); + importMenu(o); + }); + } + } + + @Override + public void create(Map params) { + coolEps.clear(); + String module = (String) params.get("module"); + String controller = (String) params.get("controller"); + String entity = (String) params.get("entity"); + String service = (String) params.get("service"); + String serviceImpl = (String) params.get("service-impl"); + String mapper = (String) params.get("mapper"); + + String fileName = (String) params.get("fileName"); + List javaPathList = new ArrayList<>(); + String modulesPath = PathUtils.getModulesPath(); + // 创建的模块地址 + String actModulePath = CompilerUtils.createModule(modulesPath, module); + // 创建顺序不能调整,类加载的时候需按这个顺序加载,否则类找不到 + // 创建 entity + String entityPath = CompilerUtils.createEntity(actModulePath, fileName, entity); + javaPathList.add(entityPath); + // 创建 mapper + javaPathList.add(CompilerUtils.createMapper(actModulePath, fileName, mapper)); + // 创建 service + javaPathList.add(CompilerUtils.createService(actModulePath, fileName, service)); + // 创建 serviceImpl + javaPathList.add(CompilerUtils.createServiceImpl(actModulePath, fileName, serviceImpl)); + // 创建 controller + javaPathList.add(CompilerUtils.createController(actModulePath, fileName, controller)); + // 构建TableDef + CompilerUtils.compilerEntityTableDef(actModulePath, fileName, entityPath, javaPathList); + // 重启 + CoolApplication.restart(javaPathList); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysParamServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysParamServiceImpl.java new file mode 100644 index 0000000..30607c6 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysParamServiceImpl.java @@ -0,0 +1,53 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.cache.CoolCache; +import com.cool.modules.base.entity.sys.BaseSysParamEntity; +import com.cool.modules.base.mapper.sys.BaseSysParamMapper; +import com.cool.modules.base.service.sys.BaseSysParamService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统参数配置 + */ +@Service +@RequiredArgsConstructor +public class BaseSysParamServiceImpl extends BaseServiceImpl + implements BaseSysParamService { + + final private CoolCache coolCache; + + @Override + public String htmlByKey(String key) { + String data = dataByKey(key); + return "" + (StrUtil.isNotEmpty(data) ? data : "key notfound") + + ""; + } + + @Override + public String dataByKey(String key) { + BaseSysParamEntity baseSysParamEntity = coolCache.get(key, BaseSysParamEntity.class); + if (baseSysParamEntity == null) { + baseSysParamEntity = getOne( + QueryWrapper.create().eq(BaseSysParamEntity::getKeyName, key)); + } + if (baseSysParamEntity != null) { + coolCache.set("param:" + baseSysParamEntity.getKeyName(), baseSysParamEntity); + return baseSysParamEntity.getData(); + } + return null; + } + + @Override + public void modifyAfter(JSONObject requestParams, BaseSysParamEntity baseSysParamEntity) { + List list = this.list(); + list.forEach(e -> { + coolCache.set("param:" + e.getKeyName(), e); + }); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysPermsServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysPermsServiceImpl.java new file mode 100644 index 0000000..42919a5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysPermsServiceImpl.java @@ -0,0 +1,262 @@ +package com.cool.modules.base.service.sys.impl; + +import static com.cool.modules.base.entity.sys.table.BaseSysMenuEntityTableDef.BASE_SYS_MENU_ENTITY; +import static com.cool.modules.base.entity.sys.table.BaseSysRoleMenuEntityTableDef.BASE_SYS_ROLE_MENU_ENTITY; +import static com.cool.modules.base.entity.sys.table.BaseSysUserRoleEntityTableDef.BASE_SYS_USER_ROLE_ENTITY; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.cool.core.cache.CoolCache; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.core.util.SpringContextUtils; +import com.cool.modules.base.entity.sys.*; +import com.cool.modules.base.mapper.sys.*; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Row; +import java.util.*; +import java.util.concurrent.ExecutorService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BaseSysPermsServiceImpl implements BaseSysPermsService { + final private CoolCache coolCache; + + final private BaseSysUserMapper baseSysUserMapper; + + final private BaseSysUserRoleMapper baseSysUserRoleMapper; + + final private BaseSysMenuMapper baseSysMenuMapper; + + final private BaseSysRoleMenuMapper baseSysRoleMenuMapper; + + final private BaseSysRoleDepartmentMapper baseSysRoleDepartmentMapper; + + final private BaseSysDepartmentMapper baseSysDepartmentMapper; + + final private ExecutorService cachedThreadPool; + + @Override + public Long[] loginDepartmentIds() { + String username = CoolSecurityUtil.getAdminUsername(); + if (username.equals("admin")) { + return baseSysDepartmentMapper.selectAll().stream().map(BaseSysDepartmentEntity::getId) + .toArray(Long[]::new); + } else { + Long[] roleIds = getRoles(username); + return baseSysRoleDepartmentMapper + .selectListByQuery( + QueryWrapper.create().in(BaseSysRoleDepartmentEntity::getRoleId, (Object) roleIds)) + .stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new); + } + } + + @Override + public Long[] getDepartmentIdsByRoleIds(Long[] roleIds) { + return getLongs(roleIds); + } + + private Long[] getLongs(Long[] roleIds) { + if (ObjectUtil.isEmpty(roleIds)) { + return new Long[]{}; + } + QueryWrapper queryWrapper = QueryWrapper.create(); + if (!CollUtil.toList(roleIds).contains(1L)) { + queryWrapper.in(BaseSysRoleDepartmentEntity::getRoleId, (Object) roleIds); + } + return baseSysRoleDepartmentMapper + .selectListByQuery(queryWrapper) + .stream().map(BaseSysRoleDepartmentEntity::getDepartmentId).toArray(Long[]::new); + } + + @Override + public Long[] getDepartmentIdsByRoleIds(Long userId) { + Long[] roleIds = getRoles(userId); + return getLongs(roleIds); + } + + @Override + public String[] getPermsCache(Long userId) { + Object result = coolCache.get("admin:perms:" + userId); + if (ObjectUtil.isNotEmpty(result)) { + return Convert.toStrArray(result); + } + return getPerms(userId); + } + + @Override + public Long[] getRoles(Long userId) { + return getRoles(baseSysUserMapper.selectOneById(userId)); + } + + @Override + public Long[] getRoles(String username) { + return getRoles( + baseSysUserMapper.selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username))); + } + + @Override + public Long[] getRoles(BaseSysUserEntity userEntity) { + Long[] roleIds = null; + if (!userEntity.getUsername().equals("admin")) { + List list = baseSysUserRoleMapper + .selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getUserId, userEntity.getId())); + roleIds = list.stream().map(BaseSysUserRoleEntity::getRoleId).toArray(Long[]::new); + if (Arrays.asList(roleIds).contains(1L)) { + roleIds = null; + } + } + return roleIds; + } + + @Override + public String[] getPerms(Long userId) { + return getPerms(getRoles(userId)); + } + + @Override + public String[] getPerms(Long[] roleIds) { + List menus = getMenus(roleIds); + Set perms = new HashSet<>(); + String[] permsData = menus.stream().map(BaseSysMenuEntity::getPerms) + .filter(itemPerms -> !StrUtil.isEmpty(itemPerms)).toArray(String[]::new); + for (String permData : permsData) { + perms.addAll(Arrays.asList(permData.split(","))); + } + return ArrayUtil.toArray(perms, String.class); + } + + @Override + public List getMenus(Long[] roleIds) { + if (CollUtil.toList(roleIds).contains(1L)) { + roleIds = null; + } + if (roleIds != null && roleIds.length == 0) { + return new ArrayList<>(); + } + + QueryWrapper queryWrapper = QueryWrapper.create().select(BASE_SYS_MENU_ENTITY.ALL_COLUMNS).from(BASE_SYS_MENU_ENTITY); + if (ObjectUtil.isNotEmpty(roleIds)) { + queryWrapper.leftJoin(BASE_SYS_ROLE_MENU_ENTITY).on(BASE_SYS_MENU_ENTITY.ID.eq(BASE_SYS_ROLE_MENU_ENTITY.MENU_ID)).and(BASE_SYS_ROLE_MENU_ENTITY.ROLE_ID.in((Object) roleIds)); + } + return baseSysMenuMapper.selectListByQuery(queryWrapper.groupBy(BASE_SYS_MENU_ENTITY.ID).orderBy(BASE_SYS_MENU_ENTITY.ORDER_NUM, false)); + } + + @Override + public List getMenus(Long userId) { + return getMenus(getRoles(userId)); + } + + @Override + public List getMenus(String username) { + BaseSysUserEntity sysUserEntity = baseSysUserMapper + .selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, username)); + return getMenus(sysUserEntity.getId()); + } + + @Override + public String[] getAllPerms() { + return getPerms((Long[]) null); + } + + @Override + public Dict permmenu(Long adminUserId) { + return Dict.create().set("menus", getMenus(adminUserId)).set("perms", getPerms(adminUserId)); + } + + @Override + public void updatePerms(Long roleId, Long[] menuIdList, Long[] departmentIds) { + // 更新菜单权限 + baseSysRoleMenuMapper.deleteByQuery(QueryWrapper.create().eq(BaseSysRoleMenuEntity::getRoleId, roleId)); + List batchRoleMenuList = new ArrayList<>(); + for (Long menuId : menuIdList) { + BaseSysRoleMenuEntity roleMenuEntity = new BaseSysRoleMenuEntity(); + roleMenuEntity.setRoleId(roleId); + roleMenuEntity.setMenuId(menuId); + batchRoleMenuList.add(roleMenuEntity); + } + if (ObjectUtil.isNotEmpty(batchRoleMenuList)) { + baseSysRoleMenuMapper.insertBatch(batchRoleMenuList); + } + // 更新部门权限 + baseSysRoleDepartmentMapper + .deleteByQuery(QueryWrapper.create().eq(BaseSysRoleDepartmentEntity::getRoleId, roleId)); + List batchRoleDepartmentList = new ArrayList<>(); + for (Long departmentId : departmentIds) { + BaseSysRoleDepartmentEntity roleDepartmentEntity = new BaseSysRoleDepartmentEntity(); + roleDepartmentEntity.setRoleId(roleId); + roleDepartmentEntity.setDepartmentId(departmentId); + batchRoleDepartmentList.add(roleDepartmentEntity); + } + if (ObjectUtil.isNotEmpty(batchRoleDepartmentList)) { + baseSysRoleDepartmentMapper.insertBatch(batchRoleDepartmentList); + } + cachedThreadPool.submit(() -> { + // 刷新对应角色用户的权限 + List userRoles = baseSysUserRoleMapper + .selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getRoleId, roleId)); + for (BaseSysUserRoleEntity userRole : userRoles) { + refreshPerms(userRole.getUserId()); + } + }); + } + + @Override + public void updateUserRole(Long userId, Long[] roleIdList) { + baseSysUserRoleMapper.deleteByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getUserId, userId)); + if (roleIdList == null) { + roleIdList = new Long[0]; + } + for (Long roleId : roleIdList) { + BaseSysUserRoleEntity sysUserRoleEntity = new BaseSysUserRoleEntity(); + sysUserRoleEntity.setRoleId(roleId); + sysUserRoleEntity.setUserId(userId); + baseSysUserRoleMapper.insert(sysUserRoleEntity); + } + refreshPerms(userId); + } + + @Override + public void refreshPerms(Long userId) { + BaseSysUserEntity baseSysUserEntity = baseSysUserMapper.selectOneById(userId); + if (baseSysUserEntity != null && baseSysUserEntity.getStatus() != 0) { + SpringContextUtils.getBean(UserDetailsService.class).loadUserByUsername(baseSysUserEntity.getUsername()); + } + if (baseSysUserEntity != null && baseSysUserEntity.getStatus() == 0) { + CoolSecurityUtil.adminLogout(baseSysUserEntity.getId(), baseSysUserEntity.getUsername()); + } + } + + @Async + @Override + public void refreshPermsByMenuId(Long menuId) { + // 刷新超管权限、 找出这个菜单的所有用户、 刷新用户权限 + BaseSysUserEntity admin = baseSysUserMapper + .selectOneByQuery(QueryWrapper.create().eq(BaseSysUserEntity::getUsername, "admin")); + refreshPerms(admin.getId()); + List list = baseSysRoleMenuMapper.selectRowsByQuery(QueryWrapper.create().select(BASE_SYS_USER_ROLE_ENTITY.USER_ID) + .from(BASE_SYS_ROLE_MENU_ENTITY).leftJoin(BASE_SYS_USER_ROLE_ENTITY) + .on(BASE_SYS_ROLE_MENU_ENTITY.ROLE_ID.eq(BASE_SYS_USER_ROLE_ENTITY.ROLE_ID)).and(BASE_SYS_ROLE_MENU_ENTITY.MENU_ID.eq(menuId, ObjectUtil.isNotEmpty(menuId))).groupBy(BASE_SYS_USER_ROLE_ENTITY.USER_ID)); + for (Row row : list) { + refreshPerms(row.getLong("userId")); + } + } + + @Override + public void refreshPermsByRoleId(Long roleId) { + // 找出角色对应的所有用户 + List list = baseSysUserRoleMapper + .selectListByQuery(QueryWrapper.create().eq(BaseSysUserRoleEntity::getRoleId, roleId)); + list.forEach(e -> { + refreshPerms(e.getUserId()); + }); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysRoleServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysRoleServiceImpl.java new file mode 100644 index 0000000..159ae97 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysRoleServiceImpl.java @@ -0,0 +1,98 @@ +package com.cool.modules.base.service.sys.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.ModifyEnum; +import com.cool.core.exception.CoolException; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.base.entity.sys.BaseSysRoleDepartmentEntity; +import com.cool.modules.base.entity.sys.BaseSysRoleEntity; +import com.cool.modules.base.entity.sys.BaseSysRoleMenuEntity; +import com.cool.modules.base.mapper.sys.BaseSysRoleDepartmentMapper; +import com.cool.modules.base.mapper.sys.BaseSysRoleMapper; +import com.cool.modules.base.mapper.sys.BaseSysRoleMenuMapper; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.cool.modules.base.service.sys.BaseSysRoleService; +import com.mybatisflex.core.query.QueryWrapper; + +import java.util.Arrays; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 系统角色 + */ +@RequiredArgsConstructor +@Service +public class BaseSysRoleServiceImpl extends BaseServiceImpl + implements BaseSysRoleService { + + final private BaseSysRoleMapper baseSysRoleMapper; + + final private BaseSysRoleMenuMapper baseSysRoleMenuMapper; + + final private BaseSysRoleDepartmentMapper baseSysRoleDepartmentMapper; + + final private BaseSysPermsService baseSysPermsService; + + @Override + public Object add(JSONObject requestParams, BaseSysRoleEntity entity) { + BaseSysRoleEntity checkLabel = getOne(QueryWrapper.create().eq(BaseSysRoleEntity::getLabel, entity.getLabel())); + if (checkLabel != null) { + throw new CoolException("标识已存在"); + } + entity.setUserId((CoolSecurityUtil.getAdminUserInfo(requestParams).getLong("userId"))); + return super.add(requestParams, entity); + } + + @Override + public BaseSysRoleEntity info(Long id) { + BaseSysRoleEntity roleEntity = getById(id); + Long[] menuIdList = new Long[0]; + Long[] departmentIdList = new Long[0]; + if (roleEntity != null) { + List list = baseSysRoleMenuMapper + .selectListByQuery(QueryWrapper.create().eq(BaseSysRoleMenuEntity::getRoleId, id, !id.equals(1L))); + menuIdList = list.stream().map(BaseSysRoleMenuEntity::getMenuId).toArray(Long[]::new); + + List departmentEntities = baseSysRoleDepartmentMapper.selectListByQuery( + QueryWrapper.create().eq(BaseSysRoleDepartmentEntity::getRoleId, id, !id.equals(1L))); + + departmentIdList = departmentEntities.stream().map(BaseSysRoleDepartmentEntity::getDepartmentId) + .toArray(Long[]::new); + + + roleEntity.setMenuIdList(Arrays.asList( menuIdList )); + roleEntity.setDepartmentIdList(Arrays.asList(departmentIdList )); + + } + + return roleEntity; + } + + @Override + public void modifyAfter(JSONObject requestParams, BaseSysRoleEntity baseSysRoleEntity, ModifyEnum type) { + if (type == ModifyEnum.DELETE) { + Long[] ids = requestParams.get("ids", Long[].class); + for (Long id : ids) { + baseSysPermsService.refreshPermsByRoleId(id); + } + } else { + baseSysPermsService.updatePerms(baseSysRoleEntity.getId(), requestParams.get("menuIdList", Long[].class), + requestParams.get("departmentIdList", Long[].class)); + } + } + + @Override + public Object list(JSONObject requestParams, QueryWrapper queryWrapper) { + return baseSysRoleMapper.selectListByQuery(queryWrapper.ne(BaseSysRoleEntity::getId, 1L).and(qw -> { + JSONObject object = CoolSecurityUtil.getAdminUserInfo(requestParams); + qw.eq(BaseSysRoleEntity::getUserId, object.get("userId")).or(w -> { + w.in(BaseSysRoleEntity::getId, + (Object) object.get("roleIds", Long[].class)); + }); + }, !CoolSecurityUtil.getAdminUsername().equals("admin"))); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysUserServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysUserServiceImpl.java new file mode 100644 index 0000000..7ba615c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/base/service/sys/impl/BaseSysUserServiceImpl.java @@ -0,0 +1,187 @@ +package com.cool.modules.base.service.sys.impl; + +import static com.cool.modules.base.entity.sys.table.BaseSysDepartmentEntityTableDef.BASE_SYS_DEPARTMENT_ENTITY; +import static com.cool.modules.base.entity.sys.table.BaseSysRoleEntityTableDef.BASE_SYS_ROLE_ENTITY; +import static com.cool.modules.base.entity.sys.table.BaseSysUserEntityTableDef.BASE_SYS_USER_ENTITY; +import static com.cool.modules.base.entity.sys.table.BaseSysUserRoleEntityTableDef.BASE_SYS_USER_ROLE_ENTITY; +import static com.mybatisflex.core.query.QueryMethods.groupConcat; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.MD5; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.ModifyEnum; +import com.cool.core.cache.CoolCache; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.core.util.DatabaseDialectUtils; +import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.mapper.sys.BaseSysDepartmentMapper; +import com.cool.modules.base.mapper.sys.BaseSysUserMapper; +import com.cool.modules.base.service.sys.BaseSysPermsService; +import com.cool.modules.base.service.sys.BaseSysUserService; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 系统用户 + */ +@Service +@RequiredArgsConstructor +public class BaseSysUserServiceImpl extends BaseServiceImpl + implements BaseSysUserService { + + final private CoolCache coolCache; + + final private BaseSysPermsService baseSysPermsService; + + final private BaseSysDepartmentMapper baseSysDepartmentMapper; + + @Override + public Object page(JSONObject requestParams, Page page, QueryWrapper qw) { + String keyWord = requestParams.getStr("keyWord"); + Integer status = requestParams.getInt("status"); + Long[] departmentIds = requestParams.get("departmentIds", Long[].class); + JSONObject tokenInfo = CoolSecurityUtil.getAdminUserInfo(requestParams); + // 用户的部门权限 + Long[] permsDepartmentArr = coolCache.get("admin:department:" + tokenInfo.get("userId"), + Long[].class); + if (DatabaseDialectUtils.isPostgresql()) { + // 兼容postgresql + qw.select("base_sys_user.id","base_sys_user.create_time","base_sys_user.department_id", + "base_sys_user.email","base_sys_user.head_img","base_sys_user.name","base_sys_user.nick_name", + "base_sys_user.phone","base_sys_user.remark","base_sys_user.status", + "base_sys_user.update_time","base_sys_user.username", + "string_agg(base_sys_role.name, ', ') AS roleName", + "base_sys_department.name AS departmentName" + ); + } else { + qw.select(BASE_SYS_USER_ENTITY.ALL_COLUMNS, + groupConcat(BASE_SYS_ROLE_ENTITY.NAME).as("roleName"), + BASE_SYS_DEPARTMENT_ENTITY.NAME.as("departmentName") + ); + } + + qw.from(BASE_SYS_USER_ENTITY).leftJoin(BASE_SYS_USER_ROLE_ENTITY) + .on(BASE_SYS_USER_ENTITY.ID.eq(BASE_SYS_USER_ROLE_ENTITY.USER_ID)) + .leftJoin(BASE_SYS_ROLE_ENTITY) + .on(BASE_SYS_USER_ROLE_ENTITY.ROLE_ID.eq(BASE_SYS_ROLE_ENTITY.ID)) + .leftJoin(BASE_SYS_DEPARTMENT_ENTITY) + .on(BASE_SYS_USER_ENTITY.DEPARTMENT_ID.eq(BASE_SYS_DEPARTMENT_ENTITY.ID)); + + // 不显示admin用户 + qw.and(BASE_SYS_USER_ENTITY.USERNAME.ne("admin")); + // 筛选部门 + qw.and(BASE_SYS_USER_ENTITY.DEPARTMENT_ID.in(departmentIds, + ArrayUtil.isNotEmpty(departmentIds))); + // 筛选状态 + qw.and(BASE_SYS_USER_ENTITY.STATUS.eq(status, status != null)); + // 搜索关键字 + if (StrUtil.isNotEmpty(keyWord)) { + qw.and(BASE_SYS_USER_ENTITY.NAME.like(keyWord) + .or(BASE_SYS_USER_ENTITY.USERNAME.like(keyWord))); + } + // 过滤部门权限 + qw.and(BASE_SYS_USER_ENTITY.DEPARTMENT_ID.in( + permsDepartmentArr == null || permsDepartmentArr.length == 0 ? new Long[]{null} + : permsDepartmentArr, + !CoolSecurityUtil.getAdminUsername().equals("admin"))); + if (DatabaseDialectUtils.isPostgresql()) { + // 兼容postgresql + qw.groupBy("base_sys_user.id","base_sys_user.create_time","base_sys_user.department_id", + "base_sys_user.email","base_sys_user.head_img","base_sys_user.name","base_sys_user.nick_name", + "base_sys_user.phone","base_sys_user.remark","base_sys_user.status", + "base_sys_user.update_time","base_sys_user.username", + "base_sys_department.name"); + } else { + qw.groupBy(BASE_SYS_USER_ENTITY.ID); + } + return mapper.paginate(page, qw); + } + + @Override + public void personUpdate(Long userId, Dict body) { + BaseSysUserEntity userEntity = getById(userId); + CoolPreconditions.checkEmpty(userEntity, "用户不存在"); + userEntity.setNickName(body.getStr("nickName")); + userEntity.setHeadImg(body.getStr("headImg")); + // 修改密码 + if (StrUtil.isNotEmpty(body.getStr("password"))) { + userEntity.setPassword(MD5.create().digestHex(body.getStr("password"))); + userEntity.setPasswordV(userEntity.getPasswordV() + 1); + coolCache.set("admin:passwordVersion:" + userId, userEntity.getPasswordV()); + } + updateById(userEntity); + } + + @Override + public void move(Long departmentId, Long[] userIds) { + UpdateChain.of(BaseSysUserEntity.class) + .set(BaseSysUserEntity::getDepartmentId, departmentId) + .in(BaseSysUserEntity::getId, (Object) userIds).update(); + } + + @Override + public Long add(JSONObject requestParams, BaseSysUserEntity entity) { + BaseSysUserEntity check = getOne( + QueryWrapper.create().eq(BaseSysUserEntity::getUsername, entity.getUsername())); + CoolPreconditions.check(check != null, "用户名已存在"); + entity.setPassword(MD5.create().digestHex(entity.getPassword())); + super.add(requestParams, entity); + return entity.getId(); + } + + @Override + public boolean update(JSONObject requestParams, BaseSysUserEntity entity) { + CoolPreconditions.check( + StrUtil.isNotEmpty(entity.getUsername()) && entity.getUsername().equals("admin"), + "非法操作"); + BaseSysUserEntity userEntity = getById(entity.getId()); + if (StrUtil.isNotEmpty(entity.getPassword())) { + entity.setPasswordV(entity.getPasswordV() + 1); + entity.setPassword(MD5.create().digestHex(entity.getPassword())); + coolCache.set("admin:passwordVersion:" + entity.getId(), entity.getPasswordV()); + } else { + entity.setPassword(userEntity.getPassword()); + entity.setPasswordV(userEntity.getPasswordV()); + } + // 被禁用 + if (entity.getStatus() == 0) { + CoolSecurityUtil.adminLogout(entity); + } + return super.update(requestParams, entity); + } + + @Override + public void modifyAfter(JSONObject requestParams, BaseSysUserEntity baseSysUserEntity, + ModifyEnum type) { + if (type != ModifyEnum.DELETE && requestParams.get("roleIdList", Long[].class) != null) { + // 刷新权限 + baseSysPermsService.updateUserRole(baseSysUserEntity.getId(), + requestParams.get("roleIdList", Long[].class)); + } + } + + @Override + public BaseSysUserEntity info(Long id) { + BaseSysUserEntity userEntity = getById(id); + Long[] roleIdList = baseSysPermsService.getRoles(id); + BaseSysDepartmentEntity departmentEntity = baseSysDepartmentMapper.selectOneById( + userEntity.getDepartmentId()); + userEntity.setPassword(null); + + + userEntity.setRoleIdList(List.of(roleIdList)); + userEntity.setDepartmentName(departmentEntity != null ? departmentEntity.getName() : userEntity.getDepartmentName() ); + + return userEntity; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java new file mode 100644 index 0000000..a9288d0 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java @@ -0,0 +1,57 @@ +package com.cool.modules.dict.controller.admin; + +import cn.hutool.core.lang.Dict; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.base.BaseController; +import com.cool.core.request.CrudOption; +import com.cool.core.request.R; +import com.cool.core.util.I18nUtil; +import com.cool.modules.dict.entity.DictInfoEntity; +import com.cool.modules.dict.entity.table.DictInfoEntityTableDef; +import com.cool.modules.dict.service.DictInfoService; +import com.mybatisflex.core.query.QueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * 字典信息 + */ +@Tag(name = "字典信息", description = "字典信息") +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) +public class AdminDictInfoController extends BaseController { + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setListOption(createOp().fieldEq(DictInfoEntityTableDef.DICT_INFO_ENTITY.TYPE_ID) + .keyWordLikeFields(DictInfoEntityTableDef.DICT_INFO_ENTITY.NAME) + .queryWrapper(QueryWrapper.create().orderBy(DictInfoEntityTableDef.DICT_INFO_ENTITY.CREATE_TIME, false)) + .transform(o -> { + DictInfoEntity entity = (DictInfoEntity) o; + entity.setName(I18nUtil.getI18nDictInfo(entity.getName())); + })); + CrudOption transform = createOp().transform(o -> { + DictInfoEntity entity = (DictInfoEntity) o; + entity.setName(I18nUtil.getI18nDictInfo(entity.getName())); + }); + setPageOption(transform); + setInfoOption(transform); + } + + @Operation(summary = "获得字典数据", description = "获得字典数据信息") + @PostMapping("/data") + public R data(@RequestBody Dict body) { + return R.ok(this.service.data(body.get("types", null))); + } + + @TokenIgnore + @GetMapping("/types") + @Operation(summary = "获得字典数据", description = "获得字典数据信息") + public R types() { + return R.ok(this.service.types()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictTypeController.java b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictTypeController.java new file mode 100644 index 0000000..fb0ff5f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/admin/AdminDictTypeController.java @@ -0,0 +1,36 @@ +package com.cool.modules.dict.controller.admin; + +import static com.cool.modules.dict.entity.table.DictTypeEntityTableDef.DICT_TYPE_ENTITY; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.CrudOption; +import com.cool.core.util.I18nUtil; +import com.cool.modules.dict.entity.DictTypeEntity; +import com.cool.modules.dict.service.DictTypeService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; + +/** + * 字典类型 + */ +@Tag(name = "字典类型", description = "字典类型") +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) +public class AdminDictTypeController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption( + createOp().select(DICT_TYPE_ENTITY.ID, DICT_TYPE_ENTITY.KEY, DICT_TYPE_ENTITY.NAME).transform(o -> { + DictTypeEntity entity = (DictTypeEntity) o; + entity.setName(I18nUtil.getI18nDictType(entity.getName())); + })); + CrudOption transform = createOp().transform(o -> { + DictTypeEntity entity = (DictTypeEntity) o; + entity.setName(I18nUtil.getI18nDictType(entity.getName())); + }); + setPageOption(transform); + setInfoOption(transform); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/controller/app/AppDictInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/app/AppDictInfoController.java new file mode 100644 index 0000000..bad4ba8 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/controller/app/AppDictInfoController.java @@ -0,0 +1,34 @@ +package com.cool.modules.dict.controller.app; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.dict.entity.DictInfoEntity; +import com.cool.modules.dict.service.DictInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 字典信息 + */ +@Tag(name = "字典信息", description = "字典信息") +@CoolRestController(api = {}) +public class AppDictInfoController extends BaseController { + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + + } + + @Operation(summary = "获得字典数据", description = "获得字典数据信息") + @PostMapping("/data") + public R data(@RequestBody Dict body) { + return R.ok(this.service.data(Convert.toList(String.class, body.get("types")))); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictInfoEntity.java b/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictInfoEntity.java new file mode 100644 index 0000000..ff0c944 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictInfoEntity.java @@ -0,0 +1,32 @@ +package com.cool.modules.dict.entity; + +import com.cool.core.base.BaseEntity; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.mybatisflex.annotation.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "dict_info", comment = "字典信息") +public class DictInfoEntity extends BaseEntity { + + @ColumnDefine(comment = "类型ID", notNull = true) + private Long typeId; + + @ColumnDefine(comment = "父ID") + private Long parentId; + + @ColumnDefine(comment = "名称", notNull = true) + private String name; + + @ColumnDefine(comment = "值") + private String value; + + @ColumnDefine(comment = "排序", defaultValue = "0") + private Integer orderNum; + + @ColumnDefine(comment = "备注") + private String remark; + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictTypeEntity.java b/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictTypeEntity.java new file mode 100644 index 0000000..35a2fd2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/entity/DictTypeEntity.java @@ -0,0 +1,21 @@ +package com.cool.modules.dict.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "dict_type", comment = "字典类型") +public class DictTypeEntity extends BaseEntity { + + @ColumnDefine(comment = "名称", notNull = true) + private String name; + + @ColumnDefine(comment = "标识", notNull = true) + @UniIndex + private String key; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictInfoMapper.java b/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictInfoMapper.java new file mode 100644 index 0000000..6c79be5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictInfoMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.dict.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.dict.entity.DictInfoEntity; + +/** + * 字典信息 + */ +public interface DictInfoMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictTypeMapper.java b/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictTypeMapper.java new file mode 100644 index 0000000..1c01714 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/mapper/DictTypeMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.dict.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.dict.entity.DictTypeEntity; + +/** + * 字典类型 + */ +public interface DictTypeMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictInfoService.java b/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictInfoService.java new file mode 100644 index 0000000..2dfec7e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictInfoService.java @@ -0,0 +1,21 @@ +package com.cool.modules.dict.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.dict.entity.DictInfoEntity; + +import java.util.List; + +/** + * 字典信息 + */ +public interface DictInfoService extends BaseService { + /** + * 字典数据 + * + * @param types 字典类型 + * @return + */ + Object data(List types); + + Object types(); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictTypeService.java b/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictTypeService.java new file mode 100644 index 0000000..0c17f82 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/service/DictTypeService.java @@ -0,0 +1,10 @@ +package com.cool.modules.dict.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.dict.entity.DictTypeEntity; + +/** + * 字典类型 + */ +public interface DictTypeService extends BaseService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictInfoServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictInfoServiceImpl.java new file mode 100644 index 0000000..51e6506 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictInfoServiceImpl.java @@ -0,0 +1,134 @@ +package com.cool.modules.dict.service.impl; + +import static com.cool.modules.dict.entity.table.DictInfoEntityTableDef.DICT_INFO_ENTITY; +import static com.cool.modules.dict.entity.table.DictTypeEntityTableDef.DICT_TYPE_ENTITY; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.ModifyEnum; +import com.cool.core.i18n.I18nGenerator; +import com.cool.modules.dict.entity.DictInfoEntity; +import com.cool.modules.dict.entity.DictTypeEntity; +import com.cool.modules.dict.mapper.DictInfoMapper; +import com.cool.modules.dict.mapper.DictTypeMapper; +import com.cool.modules.dict.service.DictInfoService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 字典信息 + */ +@Service +@RequiredArgsConstructor +public class DictInfoServiceImpl extends BaseServiceImpl implements + DictInfoService { + + final private DictTypeMapper dictTypeMapper; + + @Override + public Object data(List types) { + Dict result = Dict.create(); + QueryWrapper find = QueryWrapper.create(); + find.select(DICT_TYPE_ENTITY.ID, DICT_TYPE_ENTITY.KEY, + DICT_TYPE_ENTITY.NAME); + if (CollectionUtil.isNotEmpty(types)) { + find.and(DICT_TYPE_ENTITY.KEY.in(types)); + } + List typeData = dictTypeMapper.selectListByQuery(find); + if (typeData.isEmpty()) { + return result; + } + List infos = this.list(QueryWrapper.create() + .select(DictInfoEntity::getId, DictInfoEntity::getName, DictInfoEntity::getTypeId, + DictInfoEntity::getParentId, DictInfoEntity::getValue) + .in(DictInfoEntity::getTypeId, + typeData.stream().map(DictTypeEntity::getId).collect(Collectors.toList())) + .orderBy(DICT_INFO_ENTITY.ORDER_NUM.getName(), DICT_INFO_ENTITY.CREATE_TIME.getName())); + typeData.forEach(item -> { + List datas = new ArrayList<>(); + infos.stream().filter(d -> d.getTypeId().equals(item.getId())).toList().forEach(d -> { + Dict data = Dict.create(); + data.set("typeId", d.getTypeId()); + data.set("parentId", d.getParentId()); + data.set("name", d.getName()); + data.set("id", d.getId()); + data.set("value", StrUtil.isEmpty(d.getValue()) ? null : d.getValue()); + try { + data.set("value", Integer.parseInt(d.getValue())); + } catch (Exception ignored) { + } + datas.add(data); + }); + result.set(item.getKey(), datas); + }); + return result; + } + + @Override + public Object types() { + List infos = this.list(); + if (ObjUtil.isEmpty(infos)) { + return infos; + } + List datas = new ArrayList<>(); + infos.stream().forEach(d -> { + Dict data = Dict.create(); + data.set("typeId", d.getTypeId()); + data.set("parentId", d.getParentId()); + data.set("name", d.getName()); + data.set("id", d.getId()); + data.set("value", StrUtil.isEmpty(d.getValue()) ? null : d.getValue()); + try { + data.set("value", Integer.parseInt(d.getValue())); + } catch (Exception ignored) { + } + datas.add(data); + }); + return datas; + } + + @Override + public boolean delete(Long... ids) { + super.delete(ids); + for (Long id : ids) { + this.delDictChild(id); + } + return true; + } + + /** + * 删除子菜单 + * + * @param id 删除的菜单ID + */ + private void delDictChild(Long id) { + List delDict = list( + QueryWrapper.create().eq(DictInfoEntity::getParentId, id)); + if (CollectionUtil.isEmpty(delDict)) { + return; + } + Long[] ids = delDict.stream().map(DictInfoEntity::getId).toArray(Long[]::new); + if (ArrayUtil.isNotEmpty(ids)) { + delete(ids); + for (Long delId : ids) { + this.delDictChild(delId); + } + } + } + @Override + public void modifyAfter(JSONObject requestParams, DictInfoEntity entity, ModifyEnum type) { + if (ModifyEnum.ADD.equals(type) || ModifyEnum.UPDATE.equals(type)) { + SpringUtil.getBean(I18nGenerator.class).asyncGenBaseDictInfo(); + } + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictTypeServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictTypeServiceImpl.java new file mode 100644 index 0000000..6373cbb --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/dict/service/impl/DictTypeServiceImpl.java @@ -0,0 +1,50 @@ +package com.cool.modules.dict.service.impl; + +import static com.cool.modules.dict.entity.table.DictInfoEntityTableDef.DICT_INFO_ENTITY; +import static com.cool.modules.dict.entity.table.DictTypeEntityTableDef.DICT_TYPE_ENTITY; + +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.ModifyEnum; +import com.cool.core.i18n.I18nGenerator; +import com.cool.modules.dict.entity.DictTypeEntity; +import com.cool.modules.dict.mapper.DictInfoMapper; +import com.cool.modules.dict.mapper.DictTypeMapper; +import com.cool.modules.dict.service.DictTypeService; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 字典类型 + */ +@Service +@RequiredArgsConstructor +public class DictTypeServiceImpl extends BaseServiceImpl implements + DictTypeService { + + final private DictInfoMapper dictInfoMapper; + + @Override + public List list(QueryWrapper queryWrapper) { + return super.list( + queryWrapper.select(DICT_TYPE_ENTITY.ID, DICT_TYPE_ENTITY.KEY, + DICT_TYPE_ENTITY.NAME)); + } + + @Override + public boolean delete(Long... ids) { + super.delete(ids); + return dictInfoMapper.deleteByQuery( + QueryWrapper.create().and(DICT_INFO_ENTITY.TYPE_ID.in((Object) ids))) > 0; + } + + @Override + public void modifyBefore(JSONObject requestParams, DictTypeEntity t, ModifyEnum type) { + if (ModifyEnum.ADD.equals(type) || ModifyEnum.UPDATE.equals(type)) { + SpringUtil.getBean(I18nGenerator.class).asyncGenBaseDictType(); + } + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/aop/DeleteAspect.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/aop/DeleteAspect.java new file mode 100644 index 0000000..45003ba --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/aop/DeleteAspect.java @@ -0,0 +1,112 @@ +package com.cool.modules.recycle.aop; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.IgnoreRecycleData; +import com.cool.core.base.BaseController; +import com.cool.core.base.BaseService; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.recycle.entity.RecycleDataEntity; +import com.cool.modules.recycle.service.RecycleDataService; +import com.mybatisflex.core.query.QueryWrapper; +import jakarta.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +/** + * 数据删除前拦截 + */ +@Aspect +@Component +@Slf4j +@RequiredArgsConstructor +public class DeleteAspect { + + final private RecycleDataService recycleDataService; + + @Around(value = "execution(* com.cool.core.base.BaseController.delete*(..)) && args(request, params, requestParams)", argNames = "joinPoint,request,params,requestParams") + public Object aroundAdvice(ProceedingJoinPoint joinPoint, HttpServletRequest request, + Map params, + JSONObject requestParams) throws Throwable { + Method currentMethod = getCurrentMethod(joinPoint); + if (Objects.nonNull(currentMethod) && currentMethod.isAnnotationPresent( + IgnoreRecycleData.class)) { + // 忽略回收站记录 + return joinPoint.proceed(); + } + List list = null; + String className = null; + try { + log.info("数据删除前拦截"); + // 可以在目标方法执行前进行一些操作 + BaseController baseController = (BaseController) joinPoint.getTarget(); + BaseService service = baseController.getService(); + className = (baseController.currentEntityClass()).getName(); + QueryWrapper queryWrapper = new QueryWrapper(); + Object ids = params.get("ids"); + if (!(ids instanceof ArrayList)) { + ids = ids.toString().split(","); + } + List idList = Convert.toList(Long.class, ids); + queryWrapper.in("id", (Object) Convert.toLongArray(idList)); + list = service.list(queryWrapper); + } catch (Exception e) { + log.error("数据删除前拦截获取数据详情信息失败", e); + } + + Object result = joinPoint.proceed(); + if (ObjUtil.isNotEmpty(list)) { + RecycleDataEntity recycleDataEntity = new RecycleDataEntity(); + recycleDataEntity.setUrl(request.getRequestURI()); + recycleDataEntity.setUserName(CoolSecurityUtil.getAdminUsername()); + recycleDataEntity + .setUserId(Long.parseLong( + String.valueOf(CoolSecurityUtil.getAdminUserInfo(requestParams).get("userId")))); + recycleDataEntity.setParams(params); + recycleDataEntity.setData(list); + recycleDataEntity.setParams(params); + RecycleDataEntity.EntityInfo entityInfo = new RecycleDataEntity.EntityInfo(); + entityInfo.setEntityClassName(className); + recycleDataEntity.setEntityInfo(entityInfo); + recycleDataEntity.setCount(recycleDataEntity.getData().size()); + + log.info("数据进入回收站 {}", recycleDataService.add(recycleDataEntity)); + } + return result; + } + + private Method getCurrentMethod(JoinPoint joinPoint) { + String methodName = joinPoint.getSignature().getName(); + Object[] args = joinPoint.getArgs(); + Method[] methods = joinPoint.getTarget().getClass().getMethods(); + + for (Method method : methods) { + if (method.getName().equals(methodName) && method.getParameterCount() == args.length) { + boolean isSameMethod = true; + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + if (!parameterTypes[i].isAssignableFrom(args[i].getClass())) { + isSameMethod = false; + break; + } + } + if (isSameMethod) { + return method; + } + } + } + return null; + } + +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/controller/admin/AdminRecycleDataController.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/controller/admin/AdminRecycleDataController.java new file mode 100644 index 0000000..9e428e5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/controller/admin/AdminRecycleDataController.java @@ -0,0 +1,40 @@ +package com.cool.modules.recycle.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.recycle.entity.RecycleDataEntity; +import com.cool.modules.recycle.entity.table.RecycleDataEntityTableDef; +import com.cool.modules.recycle.service.RecycleDataService; +import com.mybatisflex.core.query.QueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.Map; + + +/** + * 数据回收站 + */ +@Tag(name = "数据回收站", description = "数据回收站") +@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) +@RequiredArgsConstructor +public class AdminRecycleDataController extends BaseController { + final private RecycleDataService recycleDataService; + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setListOption(createOp().queryWrapper(QueryWrapper.create().orderBy(RecycleDataEntityTableDef.RECYCLE_DATA_ENTITY.CREATE_TIME, false))); + } + + @Operation(summary = "恢复数据", description = "恢复数据") + @PostMapping("/restore") + public R restore(@RequestBody Map params) { + return R.ok(this.service.restore(getIds(params))); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/entity/RecycleDataEntity.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/entity/RecycleDataEntity.java new file mode 100644 index 0000000..3906b9b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/entity/RecycleDataEntity.java @@ -0,0 +1,56 @@ +package com.cool.modules.recycle.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.cool.core.mybatis.handler.Fastjson2TypeHandler; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Ignore; +import org.dromara.autotable.annotation.Index; + +/** + * 数据回收站 软删除的时候数据会回收到该表 + */ +@Getter +@Setter +@Table(value = "recycle_data", comment = "数据回收站表") +public class RecycleDataEntity extends BaseEntity { + + @ColumnDefine(comment = "表信息", type = "json") + @Column(typeHandler = Fastjson2TypeHandler.class) + private EntityInfo entityInfo; + + @Index() + @ColumnDefine(comment = "操作人", notNull = true) + private Long userId; + + @ColumnDefine(comment = "被删除的数据", type = "json") + @Column(typeHandler = Fastjson2TypeHandler.class) + private List data; + + @ColumnDefine(comment = "请求的接口", notNull = true) + private String url; + + @ColumnDefine(comment = "请求参数", type = "json", notNull = true) + @Column(typeHandler = Fastjson2TypeHandler.class) + private Map params; + + @ColumnDefine(comment = "删除数据条数", defaultValue = "1") + private Integer count; + + @Setter + @Getter + public static class EntityInfo { + + // entityClassName + public String entityClassName; + } + + @Ignore + @Column(ignore = true) // 操作人名称 + public String userName; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/mapper/RecycleDataMapper.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/mapper/RecycleDataMapper.java new file mode 100644 index 0000000..7fef289 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/mapper/RecycleDataMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.recycle.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.recycle.entity.RecycleDataEntity; + +public interface RecycleDataMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/service/RecycleDataService.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/service/RecycleDataService.java new file mode 100644 index 0000000..612584e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/service/RecycleDataService.java @@ -0,0 +1,21 @@ +package com.cool.modules.recycle.service; + +import cn.hutool.json.JSONObject; +import com.cool.core.base.BaseService; +import com.cool.modules.recycle.entity.RecycleDataEntity; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; + +import java.util.List; + +public interface RecycleDataService extends BaseService { + Object page(JSONObject requestParams, Page page, QueryWrapper queryWrapper); + + /** + * 恢复数据 + * + * @param ids + * @return + */ + Boolean restore(List ids); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/recycle/service/impl/RecycleDataServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/recycle/service/impl/RecycleDataServiceImpl.java new file mode 100644 index 0000000..bc13286 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/recycle/service/impl/RecycleDataServiceImpl.java @@ -0,0 +1,118 @@ +package com.cool.modules.recycle.service.impl; + +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.base.service.MapperProviderService; +import com.cool.modules.base.entity.sys.BaseSysUserEntity; +import com.cool.modules.base.service.sys.BaseSysUserService; +import com.cool.modules.recycle.entity.RecycleDataEntity; +import com.cool.modules.recycle.mapper.RecycleDataMapper; +import com.cool.modules.recycle.service.RecycleDataService; +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 数据回收站 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RecycleDataServiceImpl extends BaseServiceImpl + implements RecycleDataService { + + final private BaseSysUserService baseSysUserService; + + final private MapperProviderService mapperProviderService; + + @Override + public Object page(JSONObject requestParams, Page page, + QueryWrapper queryWrapper) { + String keyWord = requestParams.getStr("keyWord"); + if (ObjUtil.isNotEmpty(keyWord)) { + List list = baseSysUserService + .list(QueryWrapper.create().select(BaseSysUserEntity::getId) + .like(BaseSysUserEntity::getName, keyWord)) + .stream().map(BaseSysUserEntity::getId).toList(); + queryWrapper.like(RecycleDataEntity::getUrl, keyWord).or(w -> { + w.in(RecycleDataEntity::getUserId, list, ObjUtil.isNotEmpty(list)); + }); + } + Page iPage = page(page, queryWrapper); + List records = iPage.getRecords(); + List list = records.stream().map(RecycleDataEntity::getUserId) + .filter(ObjUtil::isNotEmpty).toList(); + + if (ObjUtil.isNotEmpty(list)) { + Map map = baseSysUserService + .list(QueryWrapper.create() + .select(BaseSysUserEntity::getId, BaseSysUserEntity::getName) + .in(BaseSysUserEntity::getId, list)) + .stream() + .collect(Collectors.toMap(BaseSysUserEntity::getId, BaseSysUserEntity::getName)); + records.forEach(o -> { + if (map.containsKey(o.getUserId())) { + o.setUserName(map.get(o.getUserId())); + } + }); + } + return iPage; + } + + @Override + public Boolean restore(List ids) { + if (ObjUtil.isEmpty(ids)) { + return false; + } + List list = list( + QueryWrapper.create().in(RecycleDataEntity::getId, ids)); + list.forEach(o -> { + // 处理恢复数据 + boolean flag = handlerRestore(o); + if (flag) { + // 删除回收站记录 + o.removeById(); + } + }); + return true; + } + + /** + * 处理数据恢复 + */ + private boolean handlerRestore(RecycleDataEntity recycleDataEntity) { + RecycleDataEntity.EntityInfo entityInfo = recycleDataEntity.getEntityInfo(); + try { + Class entityClass = ClassUtil.loadClass(entityInfo.getEntityClassName()); + List records = recycleDataEntity.getData(); + BaseMapper baseMapper = mapperProviderService.getMapperByEntityClass( + entityClass); + // 插入数据 + List insertList = new ArrayList<>(); + for (Object record : records) { + Object entity = JSONUtil.toBean(JSONUtil.parseObj(record), entityClass); + Method getIdMethod = entityClass.getMethod("getId"); + Object id = getIdMethod.invoke(entity); + if (baseMapper.selectOneById((Long) id) == null) { + insertList.add(entity); + } + } + baseMapper.insertBatch(insertList); + return true; + } catch (Exception e) { + log.error("恢复数据失败", e); + } + return false; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceInfoController.java new file mode 100644 index 0000000..af1ab9e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceInfoController.java @@ -0,0 +1,24 @@ +package com.cool.modules.space.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.space.entity.SpaceInfoEntity; +import com.cool.modules.space.service.SpaceInfoService; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletRequest; + +import static com.cool.modules.space.entity.table.SpaceInfoEntityTableDef.SPACE_INFO_ENTITY; + +/** + * 文件空间信息 + */ +@Tag(name = "文件空间信息", description = "文件空间信息") +@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) +public class AdminSpaceInfoController extends BaseController { + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp().fieldEq(SPACE_INFO_ENTITY.TYPE, SPACE_INFO_ENTITY.CLASSIFY_ID)); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceTypeController.java b/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceTypeController.java new file mode 100644 index 0000000..d5c0ace --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/controller/admin/AdminSpaceTypeController.java @@ -0,0 +1,23 @@ +package com.cool.modules.space.controller.admin; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.space.entity.SpaceTypeEntity; +import com.cool.modules.space.service.SpaceTypeService; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 文件空间信息 + */ +@Tag(name = "文件空间信息", description = "文件空间信息") +@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) +public class AdminSpaceTypeController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceInfoEntity.java b/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceInfoEntity.java new file mode 100644 index 0000000..c2de212 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceInfoEntity.java @@ -0,0 +1,57 @@ +package com.cool.modules.space.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Ignore; +import org.dromara.autotable.annotation.Index; + +/** + * 文件空间信息 + */ +@Getter +@Setter +@Table(value = "space_info", comment = "文件空间信息") +public class SpaceInfoEntity extends BaseEntity { + @ColumnDefine(comment = "地址", notNull = true) + private String url; + + @ColumnDefine(comment = "类型", notNull = true) + private String type; + + @ColumnDefine(comment = "分类ID") + private Integer classifyId; + + @Index() + @ColumnDefine(comment = "文件id") + private String fileId; + + @ColumnDefine(comment = "文件名") + private String name; + + @ColumnDefine(comment = "文件大小") + private Integer size; + + @ColumnDefine(comment = "文档版本", defaultValue = "1") + private Long version; + + @ColumnDefine(comment = "文件位置") + private String filePath; + + @Ignore + @Column(ignore = true) + private String key; + + public void setFilePath(String filePath) { + this.filePath = filePath; + this.key = filePath; + } + + public void setKey(String key) { + this.key = key; + this.filePath = key; + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceTypeEntity.java b/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceTypeEntity.java new file mode 100644 index 0000000..f50f61f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/entity/SpaceTypeEntity.java @@ -0,0 +1,21 @@ +package com.cool.modules.space.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; + +/** + * 图片空间信息分类 + */ +@Getter +@Setter +@Table(value = "space_type", comment = "图片空间信息分类") +public class SpaceTypeEntity extends BaseEntity { + @ColumnDefine(comment = "类别名称", notNull = true) + private String name; + + @ColumnDefine(comment = "父分类ID") + private Integer parentId; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceInfoMapper.java b/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceInfoMapper.java new file mode 100644 index 0000000..3f41f26 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceInfoMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.space.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.space.entity.SpaceInfoEntity; + +/** + * 文件空间信息 + */ +public interface SpaceInfoMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceTypeMapper.java b/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceTypeMapper.java new file mode 100644 index 0000000..8858289 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/mapper/SpaceTypeMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.space.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.space.entity.SpaceTypeEntity; + +/** + * 文件空间信息 + */ +public interface SpaceTypeMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceInfoService.java b/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceInfoService.java new file mode 100644 index 0000000..bbf358e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceInfoService.java @@ -0,0 +1,10 @@ +package com.cool.modules.space.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.space.entity.SpaceInfoEntity; + +/** + * 文件空间信息 + */ +public interface SpaceInfoService extends BaseService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceTypeService.java b/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceTypeService.java new file mode 100644 index 0000000..5f4aa5e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/service/SpaceTypeService.java @@ -0,0 +1,10 @@ +package com.cool.modules.space.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.space.entity.SpaceTypeEntity; + +/** + * 文件空间信息 + */ +public interface SpaceTypeService extends BaseService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceInfoServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceInfoServiceImpl.java new file mode 100644 index 0000000..de9ce6b --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceInfoServiceImpl.java @@ -0,0 +1,15 @@ +package com.cool.modules.space.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.space.entity.SpaceInfoEntity; +import com.cool.modules.space.mapper.SpaceInfoMapper; +import com.cool.modules.space.service.SpaceInfoService; +import org.springframework.stereotype.Service; + +/** + * 文件空间信息 + */ +@Service +public class SpaceInfoServiceImpl extends BaseServiceImpl + implements SpaceInfoService { +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceTypeServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceTypeServiceImpl.java new file mode 100644 index 0000000..3b5894f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/space/service/impl/SpaceTypeServiceImpl.java @@ -0,0 +1,15 @@ +package com.cool.modules.space.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.space.entity.SpaceTypeEntity; +import com.cool.modules.space.mapper.SpaceTypeMapper; +import com.cool.modules.space.service.SpaceTypeService; +import org.springframework.stereotype.Service; + +/** + * 文件空间信息 + */ +@Service +public class SpaceTypeServiceImpl extends BaseServiceImpl + implements SpaceTypeService { +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/config/ScheduleConfig.java b/cool-admin-java/src/main/java/com/cool/modules/task/config/ScheduleConfig.java new file mode 100644 index 0000000..020ab47 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/config/ScheduleConfig.java @@ -0,0 +1,21 @@ +package com.cool.modules.task.config; + +import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + + +/** + * 定时任务配置 + */ +@Configuration +@EnableScheduling +public class ScheduleConfig implements SchedulerFactoryBeanCustomizer { + @Override + public void customize(SchedulerFactoryBean schedulerFactoryBean) { + schedulerFactoryBean.setStartupDelay(2); + schedulerFactoryBean.setAutoStartup(true); + schedulerFactoryBean.setOverwriteExistingJobs(true); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/controller/admin/AdminTaskInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/task/controller/admin/AdminTaskInfoController.java new file mode 100644 index 0000000..5d3eca4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/controller/admin/AdminTaskInfoController.java @@ -0,0 +1,60 @@ +package com.cool.modules.task.controller.admin; + +import static com.cool.modules.task.entity.table.TaskInfoEntityTableDef.TASK_INFO_ENTITY; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.modules.task.entity.TaskInfoEntity; +import com.cool.modules.task.service.TaskInfoService; +import com.mybatisflex.core.paginate.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +/** + * 任务 + */ +@Tag(name = "任务管理", description = "统一管理任务") +@CoolRestController(api = { "add", "delete", "update", "info", "page" }) +public class AdminTaskInfoController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption(createOp().fieldEq(TASK_INFO_ENTITY.STATUS, TASK_INFO_ENTITY.TYPE)); + } + + @Operation(summary = "执行一次") + @PostMapping("/once") + public R once(@RequestAttribute JSONObject requestParams) { + service.once(requestParams.getLong("id")); + return R.ok(); + } + + @Operation(summary = "开始任务") + @PostMapping("/start") + public R start(@RequestAttribute JSONObject requestParams) { + service.start(requestParams.getLong("id"), requestParams.getInt("type")); + return R.ok(); + } + + @Operation(summary = "停止任务") + @PostMapping("/stop") + public R stop(@RequestAttribute JSONObject requestParams) { + service.stop(requestParams.getLong("id")); + return R.ok(); + } + + @Operation(summary = "任务日志") + @GetMapping("/log") + public R log(@RequestAttribute JSONObject requestParams) { + Integer page = requestParams.getInt("page", 0); + Integer size = requestParams.getInt("size", 20); + return R.ok(pageResult((Page) service.log(new Page<>(page, size), requestParams.getLong("id"), + requestParams.getInt("status")))); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskInfoEntity.java b/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskInfoEntity.java new file mode 100644 index 0000000..79e815c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskInfoEntity.java @@ -0,0 +1,62 @@ +package com.cool.modules.task.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "task_info", comment = "任务信息") +public class TaskInfoEntity extends BaseEntity { + /** + * 任务调度参数key + */ + @Column(ignore = true) + public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; + + @ColumnDefine(comment = "名称", notNull = true) + private String name; + + @ColumnDefine(comment = "任务ID") + private String jobId; + + @ColumnDefine(comment = "最大执行次数 不传为无限次") + private Integer repeatCount; + + @ColumnDefine(comment = "每间隔多少毫秒执行一次 如果cron设置了 这项设置就无效") + private Integer every; + + @ColumnDefine(comment = "状态 0:停止 1:运行", defaultValue = "1", notNull = true) + private Integer status; + + @ColumnDefine(comment = "服务实例名称") + private String service; + + @ColumnDefine(comment = "状态 0:cron 1:时间间隔", defaultValue = "0") + private Integer taskType; + + @ColumnDefine(comment = "状态 0:系统 1:用户", defaultValue = "0") + private Integer type; + + @ColumnDefine(comment = "任务数据") + private String data; + + @ColumnDefine(comment = "备注") + private String remark; + + @ColumnDefine(comment = "cron") + private String cron; + + @ColumnDefine(comment = "下一次执行时间") + private Date nextRunTime; + + @ColumnDefine(comment = "开始时间") + private Date startDate; + + @ColumnDefine(comment = "结束时间") + private Date endDate; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskLogEntity.java b/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskLogEntity.java new file mode 100644 index 0000000..6dd0dd2 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/entity/TaskLogEntity.java @@ -0,0 +1,29 @@ +package com.cool.modules.task.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "task_log", comment = "任务日志") +public class TaskLogEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "任务ID", notNull = true, type = "bigint") + private Long taskId; + + @ColumnDefine(comment = "状态 0:失败 1:成功", defaultValue = "0") + private Integer status; + + @ColumnDefine(comment = "详情", type = "text") + private String detail; + + // 任务名称 + @Column(ignore = true) + private String taskName; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/event/TaskEvent.java b/cool-admin-java/src/main/java/com/cool/modules/task/event/TaskEvent.java new file mode 100644 index 0000000..1ec2012 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/event/TaskEvent.java @@ -0,0 +1,25 @@ +package com.cool.modules.task.event; + +import com.cool.modules.task.service.TaskInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 事件监听 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class TaskEvent { + + final private TaskInfoService taskInfoService; + + @EventListener(ApplicationReadyEvent.class) + public void run() throws Exception { + taskInfoService.init(); + log.info("初始化任务"); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskInfoMapper.java b/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskInfoMapper.java new file mode 100644 index 0000000..770f7ea --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskInfoMapper.java @@ -0,0 +1,8 @@ +package com.cool.modules.task.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.task.entity.TaskInfoEntity; + +public interface TaskInfoMapper extends BaseMapper { + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskLogMapper.java b/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskLogMapper.java new file mode 100644 index 0000000..10a0bce --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/mapper/TaskLogMapper.java @@ -0,0 +1,8 @@ +package com.cool.modules.task.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.task.entity.TaskLogEntity; + +public interface TaskLogMapper extends BaseMapper { + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleJob.java b/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleJob.java new file mode 100644 index 0000000..f4fd06e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleJob.java @@ -0,0 +1,107 @@ +package com.cool.modules.task.run; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.cool.core.util.AutoTypeConverter; +import com.cool.modules.task.entity.TaskInfoEntity; +import com.cool.modules.task.entity.TaskLogEntity; +import com.cool.modules.task.service.TaskInfoLogService; +import com.cool.modules.task.service.TaskInfoService; +import com.mybatisflex.core.util.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobExecutionContext; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * 定时任务 + */ +@Slf4j +public class ScheduleJob extends QuartzJobBean { + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + + @Override + protected void executeInternal(JobExecutionContext context) { + // 获取spring bean + TaskInfoLogService taskInfoLogService = SpringUtil.getBean(TaskInfoLogService.class); + // 获取spring bean + TaskInfoService taskInfoService = SpringUtil.getBean(TaskInfoService.class); + + Scheduler scheduler = SpringUtil.getBean(Scheduler.class); + + TaskInfoEntity taskInfoEntity = taskInfoService + .getById(AutoTypeConverter.autoConvert(context.getJobDetail().getKey().getName().split("_")[1])); + if (ObjUtil.isEmpty(taskInfoEntity)) { + log.warn("taskInfoEntity is null"); + return; + } + + // 数据库保存执行记录 + TaskLogEntity taskLogEntity = new TaskLogEntity(); + + // 任务开始时间 + long startTime = System.currentTimeMillis(); + + try { + // 执行任务 + log.info("任务准备执行,任务ID:" + taskInfoEntity.getJobId()); + taskLogEntity.setTaskId(taskInfoEntity.getId()); + // 解析执行 + String service = taskInfoEntity.getService(); + if (StrUtil.isNotEmpty(service)) { + String[] arr = service.split("\\."); + String methodName = arr[1].substring(0, arr[1].indexOf("(")); + String params = service.substring(service.indexOf("(") + 1, service.indexOf(")")); + + ScheduleRunnable task = new ScheduleRunnable(StringUtil.firstCharToLowerCase(arr[0]).replaceAll(" ", ""), methodName, params); + Future future = executorService.submit(task); + + future.get(); + } + // 任务执行总时长 + long times = System.currentTimeMillis() - startTime; + // 状态 0:失败 1:成功 + taskLogEntity.setStatus(1); + taskLogEntity.setDetail("任务执行完毕,任务ID:" + taskInfoEntity.getJobId() + " 总共耗时:" + times + "毫秒"); + log.info(taskLogEntity.getDetail()); + } catch (Exception e) { + // 任务执行总时长 + long times = System.currentTimeMillis() - startTime; + + taskLogEntity.setDetail( + "任务执行失败,任务ID:" + taskInfoEntity.getJobId() + " 总共耗时:" + times + "毫秒" + "失败原因:" + e.getMessage()); + log.error("任务执行失败,任务ID:" + taskInfoEntity.getJobId(), e); + + // 状态 0:失败 1:成功 + taskLogEntity.setStatus(0); + } finally { + taskInfoLogService.add(taskLogEntity); + } + ThreadUtil.execAsync(() -> { + ThreadUtil.sleep(2000); + TaskInfoEntity next = new TaskInfoEntity(); + next.setId(taskInfoEntity.getId()); + try { + if (!scheduler.checkExists(context.getTrigger().getJobKey())) { + if (context.getTrigger().getNextFireTime() == null) { + next.setStatus(0); + } + } else { + if (context.getTrigger().getNextFireTime() == null) { + next.setNextRunTime(context.getTrigger().getNextFireTime()); + } + } + } catch (SchedulerException e) { + log.error("err", e); + } + taskInfoService.updateById(next); + }); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleRunnable.java b/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleRunnable.java new file mode 100644 index 0000000..dbae43c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/run/ScheduleRunnable.java @@ -0,0 +1,101 @@ +package com.cool.modules.task.run; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.cool.core.exception.CoolException; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; + +/** + * 执行定时任务 + */ +public class ScheduleRunnable implements Runnable { + private final Object target; + private final Method method; + private final Object[] params; + + public ScheduleRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringUtil.getBean(beanName); + + if (StrUtil.isNotBlank(params)) { + String[] paramArray = params.split(","); + this.params = new Object[paramArray.length]; + Class[] paramTypes = new Class[paramArray.length]; + + for (int i = 0; i < paramArray.length; i++) { + String param = paramArray[i].trim(); + if (param.matches("-?\\d+")) { + this.params[i] = Integer.parseInt(param); + paramTypes[i] = int.class; + } else if (param.matches("-?\\d+L")) { + this.params[i] = Long.parseLong(param.substring(0, param.length() - 1)); + paramTypes[i] = long.class; + } else if (param.matches("-?\\d+\\.\\d+")) { + this.params[i] = Double.parseDouble(param); + paramTypes[i] = double.class; + } else if (param.matches("-?\\d+\\.\\d+f")) { + this.params[i] = Float.parseFloat(param.substring(0, param.length() - 1)); + paramTypes[i] = float.class; + } else if (param.equalsIgnoreCase("true") || param.equalsIgnoreCase("false")) { + this.params[i] = Boolean.parseBoolean(param); + paramTypes[i] = boolean.class; + } else if (param.length() == 1) { + this.params[i] = param.charAt(0); + paramTypes[i] = char.class; + } else { + // Remove leading and trailing quotation marks for string parameters + if (param.startsWith("\"") && param.endsWith("\"")) { + param = param.substring(1, param.length() - 1); + } + this.params[i] = param; + paramTypes[i] = String.class; + } + } + + this.method = findMethod(target.getClass(), methodName, paramTypes); + } else { + this.params = new Object[0]; + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + private Method findMethod(Class targetClass, String methodName, Class[] paramTypes) throws NoSuchMethodException { + try { + return targetClass.getDeclaredMethod(methodName, paramTypes); + } catch (NoSuchMethodException e) { + // Try with wrapper classes + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] == int.class) { + paramTypes[i] = Integer.class; + } else if (paramTypes[i] == long.class) { + paramTypes[i] = Long.class; + } else if (paramTypes[i] == double.class) { + paramTypes[i] = Double.class; + } else if (paramTypes[i] == float.class) { + paramTypes[i] = Float.class; + } else if (paramTypes[i] == boolean.class) { + paramTypes[i] = Boolean.class; + } else if (paramTypes[i] == char.class) { + paramTypes[i] = Character.class; + } + } + return targetClass.getDeclaredMethod(methodName, paramTypes); + } + } + + @Override + public void run() { + try { + ReflectionUtils.makeAccessible(method); + if (params.length > 0) { + method.invoke(target, params); + } else { + method.invoke(target); + } + } catch (Exception e) { + throw new CoolException("执行定时任务失败", e); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoLogService.java b/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoLogService.java new file mode 100644 index 0000000..435c0a4 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoLogService.java @@ -0,0 +1,10 @@ +package com.cool.modules.task.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.task.entity.TaskLogEntity; + +/** + * 任务日志 + */ +public interface TaskInfoLogService extends BaseService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoService.java b/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoService.java new file mode 100644 index 0000000..6f9657c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/service/TaskInfoService.java @@ -0,0 +1,48 @@ +package com.cool.modules.task.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.task.entity.TaskInfoEntity; +import com.cool.modules.task.entity.TaskLogEntity; +import com.mybatisflex.core.paginate.Page; + +/** + * 任务信息 + */ +public interface TaskInfoService extends BaseService { + + /** + * 初始化任务 + */ + void init(); + + /** + * 执行一次 + * + * @param taskId 任务ID + */ + void once(Long taskId); + + /** + * 停止任务 + * + * @param taskId 任务ID + */ + void stop(Long taskId); + + /** + * 任务日志 + * + * @param taskId 任务ID + * @param status 任务状态 + * @return 日志列表 + */ + Object log(Page page, Long taskId, Integer status); + + /** + * 开始任务 + * + * @param taskId 任务ID + * @param type 任务类型 + */ + void start(Long taskId, Integer type); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoLogServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoLogServiceImpl.java new file mode 100644 index 0000000..d1f6ccd --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoLogServiceImpl.java @@ -0,0 +1,12 @@ +package com.cool.modules.task.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.task.entity.TaskLogEntity; +import com.cool.modules.task.mapper.TaskLogMapper; +import com.cool.modules.task.service.TaskInfoLogService; +import org.springframework.stereotype.Service; + +@Service +public class TaskInfoLogServiceImpl extends BaseServiceImpl + implements TaskInfoLogService { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoServiceImpl.java new file mode 100644 index 0000000..2d603d9 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/service/impl/TaskInfoServiceImpl.java @@ -0,0 +1,139 @@ +package com.cool.modules.task.service.impl; + +import static com.cool.modules.task.entity.table.TaskInfoEntityTableDef.TASK_INFO_ENTITY; +import static com.cool.modules.task.entity.table.TaskLogEntityTableDef.TASK_LOG_ENTITY; + +import cn.hutool.core.convert.Convert; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.task.entity.TaskInfoEntity; +import com.cool.modules.task.entity.TaskLogEntity; +import com.cool.modules.task.mapper.TaskInfoMapper; +import com.cool.modules.task.service.TaskInfoService; +import com.cool.modules.task.utils.ScheduleUtils; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TaskInfoServiceImpl extends BaseServiceImpl implements + TaskInfoService { + + final private Scheduler scheduler; + + @Override + public void init() { + try { + List list = list(); + list.forEach(scheduleJob -> { + CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, + scheduleJob.getJobId()); + if (cronTrigger == null) { + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + } else { + ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); + } + updateById(scheduleJob); + }); + } catch (Exception ignored) { + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void once(Long taskId) { + ScheduleUtils.run(scheduler, getById(taskId)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void stop(Long taskId) { + ScheduleUtils.pauseJob(scheduler, taskId + ""); + TaskInfoEntity taskInfoEntity = getById(taskId); + taskInfoEntity.setStatus(0); + updateById(taskInfoEntity); + modifyAfter(JSONUtil.parseObj(taskInfoEntity), taskInfoEntity); + } + + @Override + public Object log(Page page, Long taskId, Integer status) { + + QueryWrapper queryWrapper = QueryWrapper.create().select(TASK_LOG_ENTITY.DETAIL, + TASK_LOG_ENTITY.STATUS, TASK_LOG_ENTITY.CREATE_TIME, + TASK_INFO_ENTITY.NAME).from(TASK_LOG_ENTITY) + .leftJoin(TASK_INFO_ENTITY).on(TASK_LOG_ENTITY.TASK_ID.eq(TASK_INFO_ENTITY.ID)) + .eq(TaskLogEntity::getTaskId, taskId, taskId != null) + .eq(TaskLogEntity::getStatus, status, status != null) + .orderBy(TaskLogEntity::getCreateTime, false); + return mapper.paginateAs(page, queryWrapper, TaskLogEntity.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void start(Long taskId, Integer type) { + TaskInfoEntity taskInfoEntity = getById(taskId); + taskInfoEntity.setStatus(1); + if (type != null) { + taskInfoEntity.setType(type); + } + boolean isExists = false; + try { + isExists = scheduler.checkExists(ScheduleUtils.getJobKey(taskId + "")); + } catch (SchedulerException e) { + log.error("err", e); + } + if (isExists) { + ScheduleUtils.updateScheduleJob(scheduler, taskInfoEntity); + ScheduleUtils.resumeJob(scheduler, taskId + ""); + } else { + ScheduleUtils.createScheduleJob(scheduler, taskInfoEntity); + } + updateById(taskInfoEntity); + modifyAfter(JSONUtil.parseObj(taskInfoEntity), taskInfoEntity); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long add(JSONObject requestParams, TaskInfoEntity scheduleJob) { + scheduleJob.setStatus(1); + super.add(scheduleJob); + scheduleJob.setJobId(scheduleJob.getId() + ""); + + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + updateById(scheduleJob); + super.modifyAfter(requestParams, scheduleJob); + return scheduleJob.getId(); + } + + @Override + public boolean update(JSONObject requestParams, TaskInfoEntity entity) { + updateById(entity); + ScheduleUtils.deleteScheduleJob(scheduler, entity.getId().toString()); + if (entity.getStatus() == 1) { + start(entity.getId(), entity.getType()); + } else { + stop(entity.getId()); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delete(JSONObject requestParams, Long... ids) { + Convert.toList(String.class, ids).forEach(jobId -> { + ScheduleUtils.deleteScheduleJob(scheduler, jobId); + }); + return super.delete(requestParams, ids); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/task/utils/ScheduleUtils.java b/cool-admin-java/src/main/java/com/cool/modules/task/utils/ScheduleUtils.java new file mode 100644 index 0000000..7e82df5 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/task/utils/ScheduleUtils.java @@ -0,0 +1,251 @@ +package com.cool.modules.task.utils; + +import com.cool.core.exception.CoolException; +import com.cool.modules.task.entity.TaskInfoEntity; +import com.cool.modules.task.run.ScheduleJob; +import org.quartz.*; + +/** + * 定时任务工具类 + * + * @author Mark sunlightcs@gmail.com + * @since 1.2.0 2016-11-28 + */ +public class ScheduleUtils { + private final static String JOB_NAME = "TASK_"; + + public enum ScheduleStatus { + /** + * 暂停 + */ + PAUSE(0), + /** + * 正常 + */ + NORMAL(1); + + private int value; + + ScheduleStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * 获取触发器key + */ + public static TriggerKey getTriggerKey(String jobId) { + return TriggerKey.triggerKey(JOB_NAME + jobId); + } + + /** + * 获取jobKey + */ + public static JobKey getJobKey(String jobId) { + return JobKey.jobKey(JOB_NAME + jobId); + } + + /** + * 获取表达式触发器 + */ + public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) { + try { + return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId)); + } catch (SchedulerException e) { + throw new CoolException("获取定时任务CronTrigger出现异常", e); + } + } + + /** + * 获取表达式触发器 + */ + public static SimpleTrigger getSimpleTrigger(Scheduler scheduler, String jobId) { + try { + return (SimpleTrigger) scheduler.getTrigger(getTriggerKey(jobId)); + } catch (SchedulerException e) { + throw new CoolException("获取定时任务CronTrigger出现异常", e); + } + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, TaskInfoEntity scheduleJob) { + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())) + .build(); + + if (scheduleJob.getTaskType() == 0) { + // 表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron()) + .withMisfireHandlingInstructionDoNothing(); + + TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger() + .withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder); + + if (scheduleJob.getStartDate() != null) { + triggerBuilder.startAt(scheduleJob.getStartDate()); + } + + if (scheduleJob.getEndDate() != null) { + triggerBuilder.endAt(scheduleJob.getEndDate()); + } + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = triggerBuilder.build(); + + scheduler.scheduleJob(jobDetail, trigger); + scheduleJob.setNextRunTime(trigger.getNextFireTime()); + } + + if (scheduleJob.getTaskType() == 1) { + SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() + .withIntervalInSeconds(scheduleJob.getEvery() / 1000); + if (scheduleJob.getRepeatCount() != null) { + scheduleBuilder.withRepeatCount(scheduleJob.getRepeatCount()); + } else { + scheduleBuilder.repeatForever(); + } + TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger() + .withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder); + if (scheduleJob.getStartDate() != null) { + triggerBuilder.startAt(scheduleJob.getStartDate()); + } + + if (scheduleJob.getEndDate() != null) { + triggerBuilder.endAt(scheduleJob.getEndDate()); + } + Trigger trigger = triggerBuilder.build(); + + scheduler.scheduleJob(jobDetail, trigger); + scheduleJob.setNextRunTime(trigger.getNextFireTime()); + } + + // 暂停任务 + if (scheduleJob.getStatus() != null && scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) { + pauseJob(scheduler, scheduleJob.getJobId()); + } + } catch (SchedulerException e) { + throw new CoolException("创建定时任务失败", e); + } + } + + /** + * 更新定时任务 + */ + public static void updateScheduleJob(Scheduler scheduler, TaskInfoEntity scheduleJob) { + try { + TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId()); + + if (scheduleJob.getTaskType() == 0) { + // 表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron()) + .withMisfireHandlingInstructionDoNothing(); + + CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId()); + + TriggerBuilder triggerBuilder = trigger.getTriggerBuilder(); + + if (scheduleJob.getStartDate() != null) { + triggerBuilder.startAt(scheduleJob.getStartDate()); + } + + if (scheduleJob.getEndDate() != null) { + triggerBuilder.endAt(scheduleJob.getEndDate()); + } + + // 按新的cronExpression表达式重新构建trigger + trigger = triggerBuilder.withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + + scheduler.rescheduleJob(triggerKey, trigger); + scheduleJob.setNextRunTime(trigger.getNextFireTime()); + + } + + if (scheduleJob.getTaskType() == 1) { + SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() + .withIntervalInSeconds(scheduleJob.getEvery() / 1000); + + SimpleTrigger trigger = getSimpleTrigger(scheduler, scheduleJob.getJobId()); + + if (scheduleJob.getRepeatCount() != null) { + scheduleBuilder.withRepeatCount(scheduleJob.getRepeatCount()); + } else { + scheduleBuilder.repeatForever(); + } + TriggerBuilder triggerBuilder = trigger.getTriggerBuilder(); + if (scheduleJob.getStartDate() != null) { + triggerBuilder.startAt(scheduleJob.getStartDate()); + } + + if (scheduleJob.getEndDate() != null) { + triggerBuilder.endAt(scheduleJob.getEndDate()); + } + trigger = triggerBuilder.withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + + scheduler.rescheduleJob(triggerKey, trigger); + scheduleJob.setNextRunTime(trigger.getNextFireTime()); + } + + // 暂停任务 + if (scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) { + pauseJob(scheduler, scheduleJob.getJobId()); + } + + } catch (SchedulerException e) { + throw new CoolException("更新定时任务失败", e); + } + } + + /** + * 立即执行任务 + */ + public static void run(Scheduler scheduler, TaskInfoEntity scheduleJob) { + try { + // 参数 + JobDataMap dataMap = new JobDataMap(); + + scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap); + } catch (SchedulerException e) { + throw new CoolException("立即执行定时任务失败", e); + } + } + + /** + * 暂停任务 + */ + public static void pauseJob(Scheduler scheduler, String jobId) { + try { + scheduler.pauseJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CoolException("暂停定时任务失败", e); + } + } + + /** + * 恢复任务 + */ + public static void resumeJob(Scheduler scheduler, String jobId) { + try { + scheduler.resumeJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CoolException("恢复定时任务失败", e); + } + } + + /** + * 删除定时任务 + */ + public static void deleteScheduleJob(Scheduler scheduler, String jobId) { + try { + scheduler.deleteJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CoolException("删除定时任务失败", e); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/admin/AdminUserInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/admin/AdminUserInfoController.java new file mode 100644 index 0000000..833762d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/admin/AdminUserInfoController.java @@ -0,0 +1,24 @@ +package com.cool.modules.user.controller.admin; + +import static com.cool.modules.user.entity.table.UserInfoEntityTableDef.USER_INFO_ENTITY; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.modules.user.entity.UserInfoEntity; +import com.cool.modules.user.service.UserInfoService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; + +@Tag(name = "用户信息", description = "用户信息") +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) +public class AdminUserInfoController extends BaseController { + + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + + setPageOption(createOp().fieldEq(USER_INFO_ENTITY.STATUS, USER_INFO_ENTITY.GENDER, + USER_INFO_ENTITY.LOGIN_TYPE) + .keyWordLikeFields(USER_INFO_ENTITY.NICK_NAME, USER_INFO_ENTITY.PHONE)); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserAddressController.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserAddressController.java new file mode 100644 index 0000000..78aacc3 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserAddressController.java @@ -0,0 +1,49 @@ +package com.cool.modules.user.controller.app; + +import static com.cool.modules.user.entity.table.UserAddressEntityTableDef.USER_ADDRESS_ENTITY; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.request.R; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.modules.user.entity.UserAddressEntity; +import com.cool.modules.user.service.UserAddressService; +import com.mybatisflex.core.query.QueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * 用户模块-收货地址 + */ +@Tag(name = "用户模块-收货地址", description = "用户模块-收货地址") +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) +public class AppUserAddressController extends BaseController { + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + setPageOption( + createOp() + .queryWrapper( + QueryWrapper.create() + .and(USER_ADDRESS_ENTITY.USER_ID.eq(CoolSecurityUtil.getCurrentUserId())) + .orderBy( + USER_ADDRESS_ENTITY.IS_DEFAULT.getName(), false))); + + setListOption( + createOp() + .queryWrapper( + QueryWrapper.create() + .and(USER_ADDRESS_ENTITY.USER_ID.eq(CoolSecurityUtil.getCurrentUserId())) + .orderBy( + USER_ADDRESS_ENTITY.IS_DEFAULT.getName(), false))); + } + + @Operation(summary = "默认地址", description = "默认地址") + @GetMapping("/default") + public R getDefault() { + Long userId = CoolSecurityUtil.getCurrentUserId(); + return R.ok(this.service.getDefault(userId)); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserInfoController.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserInfoController.java new file mode 100644 index 0000000..0f09313 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserInfoController.java @@ -0,0 +1,70 @@ +package com.cool.modules.user.controller.app; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.request.R; +import com.cool.core.util.CoolSecurityUtil; +import com.cool.core.util.EntityUtils; +import com.cool.modules.user.entity.UserInfoEntity; +import com.cool.modules.user.service.UserInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; + +@RequiredArgsConstructor +@Tag(name = "用户信息", description = "用户信息") +@CoolRestController +public class AppUserInfoController { + + private final UserInfoService userInfoService; + + @Operation(summary = "用户个人信息", description = "获得App、小程序或者其他应用的用户个人信息") + @GetMapping("/person") + public R person() { + Long userId = CoolSecurityUtil.getCurrentUserId(); + UserInfoEntity userInfoEntity = userInfoService.person(userId); + return R.ok(EntityUtils.toMap(userInfoEntity, + "password")); + } + + @Operation(summary = "更新用户信息") + @PostMapping("/updatePerson") + public R updatePerson(@RequestAttribute JSONObject requestParams) { + UserInfoEntity infoEntity = requestParams.toBean(UserInfoEntity.class); + infoEntity.setId(CoolSecurityUtil.getCurrentUserId()); + return R.ok( + userInfoService.updateById(infoEntity) + ); + } + + @Operation(summary = "更新用户密码") + @PostMapping("/updatePassword") + public R updatePassword( + @RequestAttribute JSONObject requestParams + ) { + String password = requestParams.get("password", String.class); + String code = requestParams.get("code", String.class); + userInfoService.updatePassword(CoolSecurityUtil.getCurrentUserId(), password, code); + return R.ok(); + } + + @Operation(summary = "注销") + @PostMapping("/logoff") + public R logoff() { + userInfoService.logoff(CoolSecurityUtil.getCurrentUserId()); + return R.ok(); + } + + @Operation(summary = "绑定手机号") + @PostMapping("/bindPhone") + public R bindPhone( + @RequestAttribute JSONObject requestParams) { + String phone = requestParams.get("phone", String.class); + String code = requestParams.get("code", String.class); + userInfoService.bindPhone(CoolSecurityUtil.getCurrentUserId(), phone, code); + return R.ok(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserLoginController.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserLoginController.java new file mode 100644 index 0000000..53fe9d8 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/AppUserLoginController.java @@ -0,0 +1,182 @@ +package com.cool.modules.user.controller.app; + +import com.cool.core.annotation.CoolRestController; +import com.cool.core.annotation.TokenIgnore; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.request.R; +import com.cool.modules.base.service.sys.BaseSysLoginService; +import com.cool.modules.user.controller.app.params.CaptchaParam; +import com.cool.modules.user.controller.app.params.LoginParam; +import com.cool.modules.user.controller.app.params.RefreshTokenParam; +import com.cool.modules.user.controller.app.params.SmsCodeParam; +import com.cool.modules.user.service.UserLoginService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@RequiredArgsConstructor +@Tag(name = "用户登录", description = "用户登录") +@CoolRestController +public class AppUserLoginController { + + private final UserLoginService userLoginService; + + private final BaseSysLoginService baseSysLoginService; + + /** + * 小程序登录 + */ + @TokenIgnore + @Operation(summary = "小程序登录") + @PostMapping("/mini") + public R mini(@RequestBody LoginParam param) { + String code = param.getCode(); + String encryptedData = param.getEncryptedData(); + String iv = param.getIv(); + CoolPreconditions.checkEmpty(code); + CoolPreconditions.checkEmpty(encryptedData); + CoolPreconditions.checkEmpty(iv); + return R.ok(userLoginService.mini(code, encryptedData, iv)); + } + + /** + * 公众号登录 + */ + @TokenIgnore + @Operation(summary = "公众号登录") + @PostMapping("/mp") + public R mp(@RequestBody LoginParam param) { + String code = param.getCode(); + CoolPreconditions.checkEmpty(code); + return R.ok(userLoginService.mp(code)); + } + + /** + * 微信APP授权登录 + */ + @TokenIgnore + @Operation(summary = "微信APP授权登录") + @PostMapping("/wxApp") + public R wxApp(@RequestBody LoginParam param) { + String code = param.getCode(); + CoolPreconditions.checkEmpty(code); + return R.ok(userLoginService.wxApp(code)); + } + + /** + * 手机号登录 + */ + @TokenIgnore + @Operation(summary = "手机号登录") + @PostMapping("/phone") + public R phone( + @RequestBody LoginParam param) { + String phone = param.getPhone(); + String smsCode = param.getSmsCode(); + CoolPreconditions.checkEmpty(phone); + CoolPreconditions.checkEmpty(smsCode); + return R.ok(userLoginService.phoneVerifyCode(phone, smsCode)); + } + + /** + * 一键手机号登录 + */ + @TokenIgnore + @Operation(summary = "一键手机号登录") + @PostMapping("/uniPhone") + public R uniPhone( + @RequestBody LoginParam param) { + String accessToken = param.getAccess_token(); + String openid = param.getOpenid(); + String appId = param.getAppId(); + CoolPreconditions.checkEmpty(accessToken); + CoolPreconditions.checkEmpty(openid); + CoolPreconditions.checkEmpty(appId); + return R.ok(userLoginService.uniPhone(accessToken, openid, appId)); + } + + /** + * 绑定小程序手机号 + */ + @TokenIgnore + @Operation(summary = "绑定小程序手机号") + @PostMapping("/miniPhone") + public R miniPhone(@RequestBody LoginParam param) { + String code = param.getCode(); + String encryptedData = param.getEncryptedData(); + String iv = param.getIv(); + CoolPreconditions.checkEmpty(code); + CoolPreconditions.checkEmpty(encryptedData); + CoolPreconditions.checkEmpty(iv); + return R.ok(userLoginService.miniPhone(code, encryptedData, iv)); + } + + /** + * 图片验证码 + */ + @TokenIgnore + @Operation(summary = "图片验证码") + @GetMapping("/captcha") + public R captcha( + @ModelAttribute CaptchaParam param) { + String type = param.getType(); + Integer width = param.getWidth(); + Integer height = param.getHeight(); + + CoolPreconditions.checkEmpty(type); + CoolPreconditions.checkEmpty(width); + CoolPreconditions.checkEmpty(height); + + return R.ok(baseSysLoginService.captcha(UserTypeEnum.APP, type, width, height)); + } + + /** + * 验证码 + */ + @TokenIgnore + @Operation(summary = "验证码") + @PostMapping("/smsCode") + public R smsCode( + @RequestBody SmsCodeParam param) { + String phone = param.getPhone(); + String captchaId = param.getCaptchaId(); + String code = param.getCode(); + + CoolPreconditions.checkEmpty(phone); + CoolPreconditions.checkEmpty(captchaId); + CoolPreconditions.checkEmpty(code); + userLoginService.smsCode(phone, captchaId, code); + return R.ok(); + } + + /** + * 刷新token + */ + @TokenIgnore + @Operation(summary = "刷新token") + @PostMapping("/refreshToken") + public R refreshToken(@RequestBody RefreshTokenParam param) { + return R.ok(userLoginService.refreshToken(param.getRefreshToken())); + } + + /** + * 密码登录 + */ + @TokenIgnore + @Operation(summary = "密码登录") + @PostMapping("/password") + public R password( + @RequestBody LoginParam param) { + String phone = param.getPhone(); + String password = param.getPassword(); + + CoolPreconditions.checkEmpty(phone); + CoolPreconditions.checkEmpty(password); + return R.ok(userLoginService.password(phone, password)); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/CaptchaParam.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/CaptchaParam.java new file mode 100644 index 0000000..b01495c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/CaptchaParam.java @@ -0,0 +1,12 @@ +package com.cool.modules.user.controller.app.params; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class CaptchaParam { + private String type; + private Integer width = 150; + private Integer height = 50; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/LoginParam.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/LoginParam.java new file mode 100644 index 0000000..bb4df32 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/LoginParam.java @@ -0,0 +1,34 @@ +package com.cool.modules.user.controller.app.params; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class LoginParam { + + /*******小程序/公众号/微信APP授权 登录*******/ + private String code; + + private String encryptedData; + + private String iv; + + + /*******手机号登录*******/ + private String phone; + + private String smsCode; + + + /*******一键手机号登录*******/ + private String access_token; + + private String openid; + + private String appId; + + + /*******密码登录*******/ + private String password; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/RefreshTokenParam.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/RefreshTokenParam.java new file mode 100644 index 0000000..c06065a --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/RefreshTokenParam.java @@ -0,0 +1,13 @@ +package com.cool.modules.user.controller.app.params; + +import lombok.Getter; +import lombok.Setter; + +/** + * 刷新token + */ +@Setter +@Getter +public class RefreshTokenParam { + private String refreshToken; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/SmsCodeParam.java b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/SmsCodeParam.java new file mode 100644 index 0000000..c5c8c75 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/controller/app/params/SmsCodeParam.java @@ -0,0 +1,12 @@ +package com.cool.modules.user.controller.app.params; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class SmsCodeParam { + private String phone; + private String captchaId; + private String code; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserAddressEntity.java b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserAddressEntity.java new file mode 100644 index 0000000..2297a4f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserAddressEntity.java @@ -0,0 +1,43 @@ +package com.cool.modules.user.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +/** + * 用户模块-收货地址 + */ +@Getter +@Setter +@Table(value = "user_address", comment = "用户模块-收货地址") +public class UserAddressEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "用户ID", notNull = true) + private Long userId; + + @ColumnDefine(comment = "联系人", notNull = true) + private String contact; + + @Index + @ColumnDefine(comment = "手机号", length = 11, notNull = true) + private String phone; + + @ColumnDefine(comment = "省", notNull = true) + private String province; + + @ColumnDefine(comment = "市", notNull = true) + private String city; + + @ColumnDefine(comment = "区", notNull = true) + private String district; + + @ColumnDefine(comment = "地址", notNull = true) + private String address; + + @ColumnDefine(comment = "是否默认", defaultValue = "false") + private Boolean isDefault; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserInfoEntity.java b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserInfoEntity.java new file mode 100644 index 0000000..776177e --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserInfoEntity.java @@ -0,0 +1,40 @@ +package com.cool.modules.user.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Table(value = "user_info", comment = "用户信息") +public class UserInfoEntity extends BaseEntity { + + @UniIndex + @ColumnDefine(comment = "登录唯一ID") + private String unionid; + + @ColumnDefine(comment = "头像") + private String avatarUrl; + + @ColumnDefine(comment = "昵称") + private String nickName; + + @UniIndex + @ColumnDefine(comment = "手机号") + private String phone; + + @ColumnDefine(comment = "性别 0-未知 1-男 2-女", defaultValue = "0") + private Integer gender; + + @ColumnDefine(comment = "状态 0-禁用 1-正常 2-已注销", defaultValue = "1") + private Integer status; + + @ColumnDefine(comment = "登录方式 0-小程序 1-公众号 2-H5", defaultValue = "0") + private String loginType; + + @ColumnDefine(comment = "密码") + private String password; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserWxEntity.java b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserWxEntity.java new file mode 100644 index 0000000..4737c8c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/entity/UserWxEntity.java @@ -0,0 +1,47 @@ +package com.cool.modules.user.entity; + +import com.cool.core.base.BaseEntity; +import com.mybatisflex.annotation.Table; +import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine; +import com.tangzc.mybatisflex.autotable.annotation.UniIndex; +import lombok.Getter; +import lombok.Setter; +import org.dromara.autotable.annotation.Index; + +@Getter +@Setter +@Table(value = "user_wx", comment = "微信用户") +public class UserWxEntity extends BaseEntity { + + @Index + @ColumnDefine(comment = "微信unionid") + private String unionid; + + @UniIndex + @ColumnDefine(comment = "微信openid", notNull = true) + private String openid; + + @ColumnDefine(comment = "头像") + private String avatarUrl; + + @ColumnDefine(comment = "昵称") + private String nickName; + + @ColumnDefine(comment = "性别 0-未知 1-男 2-女", defaultValue = "0") + private Integer gender; + + @ColumnDefine(comment = "语言") + private String language; + + @ColumnDefine(comment = "城市") + private String city; + + @ColumnDefine(comment = "省份") + private String province; + + @ColumnDefine(comment = "国家") + private String country; + + @ColumnDefine(comment = "类型 0-小程序 1-公众号 2-H5 3-APP", defaultValue = "0") + private Integer type; +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserAddressMapper.java b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserAddressMapper.java new file mode 100644 index 0000000..94ede5c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserAddressMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.user.mapper; + +import com.cool.modules.user.entity.UserAddressEntity; +import com.mybatisflex.core.BaseMapper; + +/** + * 用户模块-收货地址 + */ +public interface UserAddressMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserInfoMapper.java b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserInfoMapper.java new file mode 100644 index 0000000..5b9f71d --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserInfoMapper.java @@ -0,0 +1,7 @@ +package com.cool.modules.user.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.user.entity.UserInfoEntity; + +public interface UserInfoMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserWxMapper.java b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserWxMapper.java new file mode 100644 index 0000000..f23a071 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/mapper/UserWxMapper.java @@ -0,0 +1,10 @@ +package com.cool.modules.user.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.user.entity.UserWxEntity; + +/** + * 微信用户 + */ +public interface UserWxMapper extends BaseMapper { +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/proxy/WxProxy.java b/cool-admin-java/src/main/java/com/cool/modules/user/proxy/WxProxy.java new file mode 100644 index 0000000..ffcba4c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/proxy/WxProxy.java @@ -0,0 +1,36 @@ +package com.cool.modules.user.proxy; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import com.cool.core.util.CoolPluginInvokers; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import org.springframework.stereotype.Service; + +@Service +public class WxProxy { + public WxMaService getWxMaService() { + return (WxMaService)CoolPluginInvokers.invoke("wx", "getWxMaService"); + } + public WxMpService getWxMpService() { + return (WxMpService)CoolPluginInvokers.invoke("wx", "getWxMpService"); + } + + public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException { + return getWxMaService().getUserService() + .getSessionInfo(jsCode); + } + + public WxMaPhoneNumberInfo getPhoneNumber(String jsCode) throws WxErrorException { + return getWxMaService().getUserService() + .getPhoneNumber(jsCode); + } + + public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) throws WxErrorException { + return getWxMaService().getUserService() + .getUserInfo(sessionKey, encryptedData, ivStr); + } + +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/UserAddressService.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserAddressService.java new file mode 100644 index 0000000..da73041 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserAddressService.java @@ -0,0 +1,15 @@ +package com.cool.modules.user.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.user.entity.UserAddressEntity; + +/** + * 用户模块-收货地址 + */ +public interface UserAddressService extends BaseService { + + /** + * 获取默认地址 + */ + Object getDefault(Long userId); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/UserInfoService.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserInfoService.java new file mode 100644 index 0000000..a779e80 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserInfoService.java @@ -0,0 +1,28 @@ +package com.cool.modules.user.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.user.entity.UserInfoEntity; + +public interface UserInfoService extends BaseService { + /** + * 用户个人信息 + * @param userId + * @return + */ + UserInfoEntity person(Long userId); + + /** + * 更新用户密码 + */ + void updatePassword(Long userId, String password, String code); + + /** + * 注销 + */ + void logoff(Long currentUserId); + + /** + * 绑定手机号 + */ + void bindPhone(Long currentUserId, String phone, String code); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/UserLoginService.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserLoginService.java new file mode 100644 index 0000000..9c8048f --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserLoginService.java @@ -0,0 +1,57 @@ +package com.cool.modules.user.service; + +/** + * 用户登录 + */ +public interface UserLoginService { + + /** + * 发送短信验证码 + * @param phone + * @param captchaId + * @param code + */ + void smsCode(String phone, String captchaId, String code); + + /** + * 手机号验证码登录 + * @param phone + * @param smsCode + */ + Object phoneVerifyCode(String phone, String smsCode); + + + /** + * 刷新token + * + * @param refreshToken 刷新token + * @return 新的token + */ + Object refreshToken(String refreshToken); + /** + * 小程序登录 + */ + Object mini(String code, String encryptedData, String iv); + /** + * 公众号登录 + */ + Object mp(String code); + /** + * 微信APP授权登录 + */ + Object wxApp(String code); + + /** + * 一键手机号登录 + */ + Object uniPhone(String accessToken, String openid, String appId); + /** + * 绑定小程序手机号 + */ + Object miniPhone(String code, String encryptedData, String iv); + + /** + * 密码登录 + */ + Object password(String phone, String password); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/UserWxService.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserWxService.java new file mode 100644 index 0000000..28e0d65 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/UserWxService.java @@ -0,0 +1,15 @@ +package com.cool.modules.user.service; + +import com.cool.core.base.BaseService; +import com.cool.modules.user.entity.UserWxEntity; + +/** + * 微信用户 + */ +public interface UserWxService extends BaseService { + + /** + * 获取小程序用户信息 + */ + UserWxEntity getMiniUserInfo(String code, String encryptedData, String iv); +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserAddressServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserAddressServiceImpl.java new file mode 100644 index 0000000..8ba4cfe --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserAddressServiceImpl.java @@ -0,0 +1,21 @@ +package com.cool.modules.user.service.impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.user.entity.UserAddressEntity; +import com.cool.modules.user.mapper.UserAddressMapper; +import com.cool.modules.user.service.UserAddressService; +import com.mybatisflex.core.query.QueryWrapper; +import org.springframework.stereotype.Service; + +/** + * 用户模块-收货地址 + */ +@Service +public class UserAddressServiceImpl extends BaseServiceImpl implements UserAddressService { + + @Override + public Object getDefault(Long userId) { + return this.getOne(QueryWrapper.create().eq(UserAddressEntity::getUserId, userId) + .eq(UserAddressEntity::getIsDefault, true).limit(1)); + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserInfoServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..e66e993 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,52 @@ +package com.cool.modules.user.service.impl; + +import cn.hutool.crypto.digest.MD5; +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.user.entity.UserInfoEntity; +import com.cool.modules.user.mapper.UserInfoMapper; +import com.cool.modules.user.service.UserInfoService; +import com.cool.modules.user.util.UserSmsUtil; +import com.cool.modules.user.util.UserSmsUtil.SendSceneEnum; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserInfoServiceImpl extends BaseServiceImpl implements + UserInfoService { + + private final UserSmsUtil userSmsUtil; + + @Override + public UserInfoEntity person(Long userId) { + UserInfoEntity info = getById(userId); + info.setPassword(null); + return info; + } + + @Override + public void updatePassword(Long userId, String password, String code) { + UserInfoEntity info = getById(userId); + userSmsUtil.checkVerifyCode(info.getPhone(), code, SendSceneEnum.ALL); + info.setPassword(MD5.create().digestHex(password)); + info.updateById(); + } + + @Override + public void logoff(Long userId) { + UserInfoEntity info = new UserInfoEntity(); + info.setId(userId); + info.setStatus(2); + info.setNickName("已注销-00" + userId); + info.updateById(); + } + + @Override + public void bindPhone(Long userId, String phone, String code) { + userSmsUtil.checkVerifyCode(phone, code, SendSceneEnum.ALL); + UserInfoEntity info = new UserInfoEntity(); + info.setId(userId); + info.setPhone(phone); + info.updateById(); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserLoginServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserLoginServiceImpl.java new file mode 100644 index 0000000..d8202a6 --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserLoginServiceImpl.java @@ -0,0 +1,192 @@ +package com.cool.modules.user.service.impl; + +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.digest.MD5; +import cn.hutool.jwt.JWT; +import com.cool.core.cache.CoolCache; +import com.cool.core.enums.UserTypeEnum; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.security.jwt.JwtTokenUtil; +import com.cool.core.security.jwt.JwtUser; +import com.cool.modules.base.service.sys.BaseSysLoginService; +import com.cool.modules.user.entity.UserInfoEntity; +import com.cool.modules.user.entity.UserWxEntity; +import com.cool.modules.user.proxy.WxProxy; +import com.cool.modules.user.service.UserInfoService; +import com.cool.modules.user.service.UserLoginService; +import com.cool.modules.user.service.UserWxService; +import com.cool.modules.user.util.UserSmsUtil; +import com.cool.modules.user.util.UserSmsUtil.SendSceneEnum; +import com.mybatisflex.core.query.QueryWrapper; +import java.util.List; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class UserLoginServiceImpl implements UserLoginService { + + private final CoolCache coolCache; + + private final JwtTokenUtil jwtTokenUtil; + + private final UserInfoService userInfoService; + + private final UserSmsUtil userSmsUtil; + + private final BaseSysLoginService baseSysLoginService; + + private final UserWxService userWxService; + + private final WxProxy wxProxy; + private final static List authority = + List.of(new SimpleGrantedAuthority("ROLE_" + UserTypeEnum.APP.name())); + + @Override + public void smsCode(String phone, String captchaId, String code) { + // 校验图片验证码,不通过直接抛异常 + baseSysLoginService.captchaCheck(captchaId, code); + userSmsUtil.sendVerifyCode(phone, SendSceneEnum.ALL); + coolCache.del("verify:img:" + captchaId); + } + + @Override + public Object phoneVerifyCode(String phone, String smsCode) { + // 校验短信验证码,不通过直接抛异常 + userSmsUtil.checkVerifyCode(phone, smsCode, SendSceneEnum.ALL); + return generateTokenByPhone(phone); + } + + + @Override + public Object refreshToken(String refreshToken) { + CoolPreconditions.check(!jwtTokenUtil.validateRefreshToken(refreshToken), "错误的refreshToken"); + JWT jwt = jwtTokenUtil.getTokenInfo(refreshToken); + CoolPreconditions.check(jwt == null || !(Boolean) jwt.getPayload("isRefresh"), + "错误的refreshToken"); + Long userId = Convert.toLong(jwt.getPayload("userId")); + return generateToken(userId, refreshToken); + } + + @Override + public Object mini(String code, String encryptedData, String iv) { + UserWxEntity userWxEntity = userWxService.getMiniUserInfo(code, encryptedData, iv); + return wxLoginToken(userWxEntity); + } + + private Object wxLoginToken(UserWxEntity userWxEntity) { + String unionId = ObjUtil.isNotEmpty(userWxEntity.getUnionid()) ? userWxEntity.getUnionid() + : userWxEntity.getOpenid(); + UserInfoEntity userInfoEntity = userInfoService.getOne( + QueryWrapper.create().eq(UserInfoEntity::getUnionid, unionId)); + if (ObjUtil.isEmpty(userInfoEntity)) { + userInfoEntity = new UserInfoEntity(); + userInfoEntity.setNickName(ObjUtil.isNotEmpty(userWxEntity.getNickName()) ? userWxEntity.getNickName() : generateRandomNickname()); + userInfoEntity.setGender(userWxEntity.getGender()); + userInfoEntity.setAvatarUrl(userWxEntity.getAvatarUrl()); + userInfoEntity.setUnionid(unionId); + userInfoEntity.save(); + } + return generateToken(userInfoEntity, null); + } + + @Override + public Object mp(String code) { + return null; + } + + @Override + public Object wxApp(String code) { + return null; + } + + @Override + public Object uniPhone(String accessToken, String openid, String appId) { + return null; + } + + @Override + public Object miniPhone(String code, String encryptedData, String iv) { + try { + WxMaPhoneNumberInfo phoneNumber = wxProxy.getPhoneNumber(code); + CoolPreconditions.checkEmpty(phoneNumber, "微信登录失败"); + return generateTokenByPhone(phoneNumber.getPhoneNumber()); + } catch (WxErrorException e) { + CoolPreconditions.alwaysThrow(e.getMessage(), e); + } + CoolPreconditions.alwaysThrow("微信登录失败"); + return null; + } + + @Override + public Object password(String phone, String password) { + UserInfoEntity userInfoEntity = userInfoService.getOne( + QueryWrapper.create().eq(UserInfoEntity::getPhone, phone)); + CoolPreconditions.checkEmpty(userInfoEntity, "账号或密码错误"); + if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) { + return generateToken(userInfoEntity, null); + } + CoolPreconditions.checkEmpty(userInfoEntity, "账号或密码错误"); + return null; + } + + /** + * 前置已校验用户的手机号, + * 根据手机号找到用户生成token + */ + private Object generateTokenByPhone(String phone) { + UserInfoEntity userInfoEntity = userInfoService.getOne( + QueryWrapper.create().eq(UserInfoEntity::getPhone, phone)); + if (ObjUtil.isEmpty(userInfoEntity)) { + userInfoEntity = new UserInfoEntity(); + userInfoEntity.setPhone(phone); + // 生成随机昵称 + userInfoEntity.setNickName(generateRandomNickname()); + userInfoEntity.save(); + } + return generateToken(userInfoEntity, null); + } + + /** + * + * @return 生成的昵称 + */ + private String generateRandomNickname() { + // 定义昵称的长度 + int length = 8; + // 生成随机字符串 + return RandomUtil.randomString(length); + } + /** + * 生成token + */ + private Dict generateToken(Long userId, String refreshToken) { + UserInfoEntity userInfoEntity = userInfoService.getById(userId); + return generateToken(userInfoEntity, refreshToken); + } + private Dict generateToken(UserInfoEntity userInfoEntity, String refreshToken) { + Dict tokenInfo = Dict.create() + .set("userType", UserTypeEnum.APP.name()) + .set("userId", userInfoEntity.getId()); + String token = jwtTokenUtil.generateToken(tokenInfo); + if (ObjUtil.isEmpty(refreshToken)) { + refreshToken = jwtTokenUtil.generateRefreshToken(tokenInfo); + } + JwtUser jwtUser = new JwtUser(userInfoEntity.getId(), + authority, + ObjUtil.equals(userInfoEntity.getStatus(), 1)); + coolCache.set("app:userDetails:" + jwtUser.getUserId(), jwtUser); + return Dict.create() + .set("token", token) + .set("expire", jwtTokenUtil.getExpire()) + .set("refreshToken", refreshToken) + .set("refreshExpire", jwtTokenUtil.getRefreshExpire()); + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserWxServiceImpl.java b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserWxServiceImpl.java new file mode 100644 index 0000000..6b8286c --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/service/impl/UserWxServiceImpl.java @@ -0,0 +1,60 @@ +package com.cool.modules.user.service.impl; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjUtil; +import com.cool.core.base.BaseServiceImpl; +import com.cool.core.exception.CoolPreconditions; +import com.cool.modules.user.entity.UserWxEntity; +import com.cool.modules.user.mapper.UserWxMapper; +import com.cool.modules.user.proxy.WxProxy; +import com.cool.modules.user.service.UserWxService; +import com.mybatisflex.core.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.stereotype.Service; + +/** + * 微信用户 + */ +@Service +@RequiredArgsConstructor +public class UserWxServiceImpl extends BaseServiceImpl implements UserWxService { + + private final WxProxy wxProxy; + + /** + * 获得小程序用户信息 + */ + public UserWxEntity getMiniUserInfo(String code, String encryptedData, String iv) { + // 获取 session + WxMaJscode2SessionResult result = null; + try { + result = wxProxy.getSessionInfo(code); + // 解密数据 + WxMaUserInfo wxMaUserInfo = wxProxy.getUserInfo(result.getSessionKey(), encryptedData, iv); + if (ObjUtil.isNotEmpty(wxMaUserInfo)) { + UserWxEntity userWxEntity = BeanUtil.copyProperties(wxMaUserInfo, UserWxEntity.class); + userWxEntity.setOpenid(result.getOpenid()); + userWxEntity.setUnionid(wxMaUserInfo.getUnionId()); + return getBySave(userWxEntity, 0); + } + } catch (WxErrorException e) { + CoolPreconditions.alwaysThrow(e.getMessage(), e); + } + CoolPreconditions.alwaysThrow("获得小程序用户信息"); + return null; + } + + public UserWxEntity getBySave(UserWxEntity entity, int type) { + UserWxEntity one = this.getOne( + QueryWrapper.create().eq(UserWxEntity::getOpenid, entity.getOpenid())); + if (ObjUtil.isEmpty(one)) { + entity.setType(type); + super.save(entity); + return entity; + } + return one; + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/util/UserSmsUtil.java b/cool-admin-java/src/main/java/com/cool/modules/user/util/UserSmsUtil.java new file mode 100644 index 0000000..43c5aff --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/util/UserSmsUtil.java @@ -0,0 +1,90 @@ +package com.cool.modules.user.util; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.cool.core.cache.CoolCache; +import com.cool.core.exception.CoolPreconditions; +import com.cool.core.plugin.service.CoolPluginService; +import com.cool.core.util.CoolPluginInvokers; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * UserSmsUtil - 用户短信工具类 + * 该类用于发送短信验证码。 + */ +@RequiredArgsConstructor +@Slf4j +@Component +public class UserSmsUtil { + + /** + * 短信发送场景枚举 + */ + public enum SendSceneEnum { + ALL, + } + + private final CoolPluginService coolPluginService; + + private final CoolCache coolCache; + + /** + * 发送短信验证码 + * + * @param phone + */ + public void sendVerifyCode(String phone, SendSceneEnum sendSceneEnum) { + // 随机生成4位验证码 + String verifyCode = RandomUtil.randomNumbers(4); + send(phone, verifyCode); + coolCache.set(sendSceneEnum.name() + "_sms:" + phone, verifyCode, 60 * 10); + } + + /** + * 检查验证码 + * @param phone + * @param code + * @return + */ + public void checkVerifyCode(String phone, String code, SendSceneEnum sendSceneEnum) { + String key = sendSceneEnum.name() + "_sms:" + phone; + String cacheCode = coolCache.get(key, String.class); + boolean flag = StrUtil.isNotEmpty(code) && code.equals(cacheCode); + if (flag) { + // 删除验证码 + coolCache.del(key); + } + CoolPreconditions.check(!flag, "验证码错误"); + } + + /** + * 发送短信 + * + * @param phone + * @param code + */ + public void send(String phone, String code) { + List phones = new ArrayList<>(); + phones.add(phone); + + Map params = new HashMap<>(); + params.put("code", code); + // 插件key sms-tx、sms-ali,哪个实例存在就调用哪个 + if (coolPluginService.getInstanceWithoutCheck("sms-tx") != null) { + // 调用腾讯短信插件 + CoolPluginInvokers.invoke("sms-tx", "send", phones, params); + } else if (coolPluginService.getInstanceWithoutCheck("sms-ali") != null) { + // 调用阿里短信插件 + CoolPluginInvokers.invoke("sms-ali", "send", phones, params); + } else { + // 未找到短信插件 + log.error("未找到短信插件,请前往插件市场下载安装"); + } + } +} diff --git a/cool-admin-java/src/main/java/com/cool/modules/user/util/UserWxUtil.java b/cool-admin-java/src/main/java/com/cool/modules/user/util/UserWxUtil.java new file mode 100644 index 0000000..74b32eb --- /dev/null +++ b/cool-admin-java/src/main/java/com/cool/modules/user/util/UserWxUtil.java @@ -0,0 +1,12 @@ +package com.cool.modules.user.util; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * UserWxUtil - 用户微信工具类 + */ +@Component +@RequiredArgsConstructor +public class UserWxUtil { +} diff --git a/cool-admin-java/src/main/java/org/dromara/autotable/core/strategy/pgsql/builder/ColumnSqlBuilder.java b/cool-admin-java/src/main/java/org/dromara/autotable/core/strategy/pgsql/builder/ColumnSqlBuilder.java new file mode 100644 index 0000000..97f545c --- /dev/null +++ b/cool-admin-java/src/main/java/org/dromara/autotable/core/strategy/pgsql/builder/ColumnSqlBuilder.java @@ -0,0 +1,53 @@ +package org.dromara.autotable.core.strategy.pgsql.builder; + +import org.dromara.autotable.annotation.enums.DefaultValueEnum; +import org.dromara.autotable.core.strategy.ColumnMetadata; +import org.dromara.autotable.core.utils.StringConnectHelper; +import org.dromara.autotable.core.utils.StringUtils; + +/** + * 列相关的SQL生成器 + * 覆盖原始的ColumnSqlBuilder GENERATED ALWAYS AS IDENTITY 改为 GENERATED BY DEFAULT AS IDENTITY + * 已提issues https://gitee.com/dromara/auto-table/issues/IC6TR5 + */ +public class ColumnSqlBuilder { + + /** + * 生成字段相关的SQL片段 + * + * @param columnMetadata 列元数据 + * @return 列相关的sql + */ + public static String buildSql(ColumnMetadata columnMetadata) { + // 例子:"name" varchar(100) NULL DEFAULT '张三' COMMENT '名称' + // 例子:"id" int4(32) NOT NULL AUTO_INCREMENT COMMENT '主键' + StringConnectHelper sql = StringConnectHelper.newInstance("{columnName} {typeAndLength} {null} {default}") + .replace("{columnName}", columnMetadata.getName()) + .replace("{typeAndLength}", () -> columnMetadata.getType().getDefaultFullType()); + + // 如果是自增列,则使用GENERATED ALWAYS AS IDENTITY, 忽略not null和默认值的配置 + if (columnMetadata.isAutoIncrement()) { + return sql.replace("{null} {default}", "GENERATED BY DEFAULT AS IDENTITY").toString(); + } + + return sql.replace("{null}", columnMetadata.isNotNull() ? "NOT NULL" : "") + .replace("{default}", () -> { + // 指定NULL + DefaultValueEnum defaultValueType = columnMetadata.getDefaultValueType(); + if (defaultValueType == DefaultValueEnum.NULL) { + return "DEFAULT NULL"; + } + // 指定空字符串 + if (defaultValueType == DefaultValueEnum.EMPTY_STRING) { + return "DEFAULT ''"; + } + // 自定义 + String defaultValue = columnMetadata.getDefaultValue(); + if (DefaultValueEnum.isCustom(defaultValueType) && StringUtils.hasText(defaultValue)) { + return "DEFAULT " + defaultValue; + } + return ""; + }) + .toString(); + } +} diff --git a/cool-admin-java/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java b/cool-admin-java/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java new file mode 100644 index 0000000..2d01e23 --- /dev/null +++ b/cool-admin-java/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java @@ -0,0 +1,159 @@ +/* + * Copyright 2012-2022 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 + * + * https://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 org.springframework.boot.autoconfigure.quartz; + +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; + +import com.cool.core.util.DatabaseDialectUtils; +import org.quartz.Calendar; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.Trigger; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Quartz Scheduler. + * 覆盖原始的QuartzAutoConfiguration,兼容postgres数据库 + * properties.getProperties().put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"); + * @author Vedran Pavic + * @author Stephane Nicoll + * @since 2.0.0 + */ +@AutoConfiguration(after = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, + LiquibaseAutoConfiguration.class, FlywayAutoConfiguration.class }) +@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class }) +@EnableConfigurationProperties(QuartzProperties.class) +public class QuartzAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public SchedulerFactoryBean quartzScheduler(QuartzProperties properties, + ObjectProvider customizers, ObjectProvider jobDetails, + Map calendars, ObjectProvider triggers, ApplicationContext applicationContext) { + SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); + SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); + jobFactory.setApplicationContext(applicationContext); + schedulerFactoryBean.setJobFactory(jobFactory); + if (properties.getSchedulerName() != null) { + schedulerFactoryBean.setSchedulerName(properties.getSchedulerName()); + } + schedulerFactoryBean.setAutoStartup(properties.isAutoStartup()); + schedulerFactoryBean.setStartupDelay((int) properties.getStartupDelay().getSeconds()); + schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(properties.isWaitForJobsToCompleteOnShutdown()); + schedulerFactoryBean.setOverwriteExistingJobs(properties.isOverwriteExistingJobs()); + if (!properties.getProperties().isEmpty()) { + schedulerFactoryBean.setQuartzProperties(asProperties(properties.getProperties())); + } + schedulerFactoryBean.setJobDetails(jobDetails.orderedStream().toArray(JobDetail[]::new)); + schedulerFactoryBean.setCalendars(calendars); + schedulerFactoryBean.setTriggers(triggers.orderedStream().toArray(Trigger[]::new)); + customizers.orderedStream().forEach((customizer) -> customizer.customize(schedulerFactoryBean)); + return schedulerFactoryBean; + } + + private Properties asProperties(Map source) { + Properties properties = new Properties(); + properties.putAll(source); + return properties; + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnSingleCandidate(DataSource.class) + @ConditionalOnProperty(prefix = "spring.quartz", name = "job-store-type", havingValue = "jdbc") + @Import(DatabaseInitializationDependencyConfigurer.class) + protected static class JdbcStoreTypeConfiguration { + + @Bean + @Order(0) + public SchedulerFactoryBeanCustomizer dataSourceCustomizer(QuartzProperties properties, DataSource dataSource, + @QuartzDataSource ObjectProvider quartzDataSource, + ObjectProvider transactionManager, + @QuartzTransactionManager ObjectProvider quartzTransactionManager) { + return (schedulerFactoryBean) -> { + DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource); + schedulerFactoryBean.setDataSource(dataSourceToUse); + PlatformTransactionManager txManager = getTransactionManager(transactionManager, + quartzTransactionManager); + if (txManager != null) { + schedulerFactoryBean.setTransactionManager(txManager); + } + }; + } + + private DataSource getDataSource(DataSource dataSource, ObjectProvider quartzDataSource) { + DataSource dataSourceIfAvailable = quartzDataSource.getIfAvailable(); + return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource; + } + + private PlatformTransactionManager getTransactionManager( + ObjectProvider transactionManager, + ObjectProvider quartzTransactionManager) { + PlatformTransactionManager transactionManagerIfAvailable = quartzTransactionManager.getIfAvailable(); + return (transactionManagerIfAvailable != null) ? transactionManagerIfAvailable + : transactionManager.getIfUnique(); + } + + @Bean + @ConditionalOnMissingBean(QuartzDataSourceScriptDatabaseInitializer.class) + @Conditional(OnQuartzDatasourceInitializationCondition.class) + public QuartzDataSourceScriptDatabaseInitializer quartzDataSourceScriptDatabaseInitializer( + DataSource dataSource, @QuartzDataSource ObjectProvider quartzDataSource, + QuartzProperties properties) { + DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource); + if (DatabaseDialectUtils.isPostgresql(dataSource)) { + properties.getProperties().put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"); + } + return new QuartzDataSourceScriptDatabaseInitializer(dataSourceToUse, properties); + } + + static class OnQuartzDatasourceInitializationCondition extends OnDatabaseInitializationCondition { + + OnQuartzDatasourceInitializationCondition() { + super("Quartz", "spring.quartz.jdbc.initialize-schema"); + } + + } + + } + +} diff --git a/cool-admin-java/src/main/resources/application-local.yml b/cool-admin-java/src/main/resources/application-local.yml new file mode 100644 index 0000000..049731f --- /dev/null +++ b/cool-admin-java/src/main/resources/application-local.yml @@ -0,0 +1,42 @@ +spring: + datasource: + url: jdbc:mysql://107.172.82.181:63301/cool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8 + username: root + password: W4%qmVpow + driver-class-name: com.mysql.cj.jdbc.Driver + +# url: jdbc:postgresql://127.0.0.1:5432/cool +# username: postgres +# password: 123456 +# driver-class-name: org.postgresql.Driver +# AutoTable配置,根据实体类自动生成表 +auto-table: + # 启用自动维护表功能 + enable: true + # 自动删除名称不匹配的索引 + autoDropIndex: true + # 建表的时候,父类的字段排序是在子类后面还是前面 + superInsertPosition: before + # 模型包路径 + model-package: com.cool.**.entity.* + +# Cool相关配置 +cool: + # 初始化数据 + initData: false + +# 文档 +springdoc: + api-docs: + #是否开启文档功能 本地为了配合eps功能不可关闭 + enabled: true + group-configs: + - group: app + paths-to-match: /app/** + - group: admin + paths-to-match: /admin/** + +# 设置日志级别 +logging: + level: + com.cool: debug \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/application-prod.yml b/cool-admin-java/src/main/resources/application-prod.yml new file mode 100644 index 0000000..7d4b34d --- /dev/null +++ b/cool-admin-java/src/main/resources/application-prod.yml @@ -0,0 +1,27 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/cool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8 + username: root + password: 123456 + driver-class-name: com.mysql.cj.jdbc.Driver + +# AutoTable配置,根据实体类自动生成表 +auto-table: + # 启用自动维护表功能 + enable: false + +# Cool相关配置 +cool: + # 初始化数据 + initData: false + +# 文档 +springdoc: + api-docs: + #是否开启文档功能 本地为了配合eps功能不可关闭 + enabled: true + +# 设置日志级别 +logging: + level: + com.cool: error \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/application.yml b/cool-admin-java/src/main/resources/application.yml new file mode 100644 index 0000000..2ca78b3 --- /dev/null +++ b/cool-admin-java/src/main/resources/application.yml @@ -0,0 +1,162 @@ +server: + port: 8001 + servlet: + context-path: / + compression: + enabled: true + mime-types: application/json,application/xml,text/html,text/plain + +spring: + application: + name: cool-admin-java + profiles: + active: @spring.active@ + thymeleaf: + cache: false + prefix: classpath:/templates/ + suffix: .html + mode: HTML + #返回时间格式化 + jackson: + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + + servlet: + multipart: + enabled: true + max-file-size: 2GB + max-request-size: 2GB + # Web设置 + web: + resources: + add-mappings: true + static-locations: classpath:/static/,file:./assets/public/ + + # caffeine 缓存 + cache: + type: caffeine + file: assets/cache + + #redis 缓存 + cache: + type: redis + data: + redis: + host: 107.172.82.181 + port: 63179 + database: 8 + password: 'R30#B5nTT' + quartz: + job-store-type: jdbc + jdbc: + initialize-schema: always + autoStartup: true + #相关属性配置 + properties: + org: + quartz: + scheduler: + instanceName: CoolScheduler + instanceId: AUTO + jobStore: + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: true + clusterCheckinInterval: 10000 + useProperties: false + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 5 + threadPriority: 9 + threadsInheritContextClassLoaderOfInitializingThread: true + +# 忽略url +ignored: + # 忽略后台鉴权url + adminAuthUrls: + - / + - /upload/** + - /actuator/** + - /download/** + - /static/** + - /favicon.ico + - /v3/api-docs/** + - /swagger + - /swagger-ui/** + - /css/* + - /js/* + - /druid/** + - /admin/base/open/** + # 忽略记录请求日志url + logUrls: + - / + - /**/eps + - /app/** + - /css/* + - /js/* + - /favicon.ico +# 文档 +springdoc: + api-docs: + #swagger后端请求地址 + path: /v3/api-docs + swagger-ui: + #自定义swagger前端请求路径,输入http://127.0.0.1:端口号/swagger会自动重定向到swagger页面 + path: /swagger + +mybatis-flex: + #多数据源 + # datasource: + #MyBatis 配置文件位置,如果有单独的 MyBatis 配置,需要将其路径配置到 configLocation 中 + # configuration: + #MyBatis Mapper 所对应的 XML 文件位置,如果在 Mapper 中有自定义的方法(XML 中有自定义的实现),需要进行该配置,指定 Mapper 所对应的 XML 文件位置 + mapper-locations: [ "classpath*:/mapper/**/*.xml" ] + type-aliases-package: com.cool.**.entity.* + global-config: + print-banner: false + + +# Cool相关配置 +cool: + # 缓存名称 + cacheName: comm + plugin: + # 插件安装位置 + path: assets/plugin + # token 相关配置 + token: + # 过期时间 单位:秒 半小时 + expire: 1800 + # 刷新token过期时间 单位:秒 7天 + refreshExpire: 604800 + # 文件上传相关 + file: + #上传模式 + mode: local + # 本地上传配置 + local: + # 文件访问地址 + base-url: http://127.0.0.1:${server.port}/upload + # 系统日志请求参数超过1024字节 就不记录,避免日志过大 + log: + # 请求参数最大字节,超过请求参数不记录 + max-byte-length: 1024 + # 核心线程数的倍数 + core-pool-size-multiplier: 2 + # 最大线程数的倍数 + max-pool-size-multiplier: 3 + # 队列容量的倍数 + queue-capacity-multiplier: 3 + multi-tenant: + # 是否开启多租户,默认关闭 + enable: false +# AutoTable配置,根据实体类自动生成表 +auto-table: + show-banner: false + +# 分布式唯一ID组件 +leaf: + segment: + # 默认禁用 + enable: false \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/banner.txt b/cool-admin-java/src/main/resources/banner.txt new file mode 100644 index 0000000..0fce0d9 --- /dev/null +++ b/cool-admin-java/src/main/resources/banner.txt @@ -0,0 +1,8 @@ +✨🌈✨[cool-admin-java-plus](https://gitee.com/hlc4417/cool-admin-java-plus)✨🌈✨ + ______ ___ ___ _____ _ ______ ____ ____ _____ ____ _____ + .' ___ | .' `. .' `.|_ _| V8.x / \ |_ _ `.|_ \ / _||_ _||_ \|_ _| +/ .' \_|/ .-. \/ .-. \ | | ______ / _ \ | | `. \ | \/ | | | | \ | | +| | | | | || | | | | | _|______|/ ___ \ | | | | | |\ /| | | | | |\ \| | +\ `.___.'\\ `-' /\ `-' /_| |__/ | _/ / \ \_ _| |_.' /_| |_\/_| |_ _| |_ _| |_\ |_ + `.____ .' `.___.' `.___.'|________| |____| |____||______.'|_____||_____||_____||_____|\____| +:: https://java.cool-admin.com :: \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/48.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/48.jpg new file mode 100644 index 0000000..ae731e4 Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/48.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/a.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/a.jpg new file mode 100644 index 0000000..cef38ec Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/a.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/b.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/b.jpg new file mode 100644 index 0000000..72fdb56 Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/b.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/c.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/c.jpg new file mode 100644 index 0000000..7c8274f Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/c.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/d.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/d.jpg new file mode 100644 index 0000000..6057faa Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/d.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/e.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/e.jpg new file mode 100644 index 0000000..b5e22e3 Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/e.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/g.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/g.jpg new file mode 100644 index 0000000..ae707ba Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/g.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/h.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/h.jpg new file mode 100644 index 0000000..2326c88 Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/h.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/i.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/i.jpg new file mode 100644 index 0000000..f9714d9 Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/i.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/captcha/bgimages/j.jpg b/cool-admin-java/src/main/resources/cool/captcha/bgimages/j.jpg new file mode 100644 index 0000000..8f9056f Binary files /dev/null and b/cool-admin-java/src/main/resources/cool/captcha/bgimages/j.jpg differ diff --git a/cool-admin-java/src/main/resources/cool/code/controller.th b/cool-admin-java/src/main/resources/cool/code/controller.th new file mode 100644 index 0000000..27cf579 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/code/controller.th @@ -0,0 +1,22 @@ +package com.cool.modules.[(${module})].controller.[(${type})][(${subModule}?'.'+${subModule}:'')]; + +import cn.hutool.json.JSONObject; +import com.cool.core.annotation.CoolRestController; +import com.cool.core.base.BaseController; +import com.cool.core.enums.Apis; +import com.cool.modules.[(${module})].entity[(${subModule}?'.'+${subModule}:'')].[(${entity})]Entity; +import com.cool.modules.[(${module})].service[(${subModule}?'.'+${subModule}:'')].[(${entity})]Service; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; + +/** + * [(${name})] + */ +@Tag(name = "[(${name})]", description = "[(${name})]") +@CoolRestController(api = {Apis.ADD, Apis.DELETE, Apis.UPDATE, Apis.PAGE, Apis.LIST, Apis.INFO}) +public class [(${upperType})][(${entity})]Controller extends BaseController<[(${entity})]Service, [(${entity})]Entity> { + @Override + protected void init(HttpServletRequest request, JSONObject requestParams) { + + } +} \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/cool/code/mapper/interface.th b/cool-admin-java/src/main/resources/cool/code/mapper/interface.th new file mode 100644 index 0000000..112e6ab --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/code/mapper/interface.th @@ -0,0 +1,10 @@ +package com.cool.modules.[(${module})].mapper[(${subModule}?'.'+${subModule}:'')]; + +import com.mybatisflex.core.BaseMapper; +import com.cool.modules.[(${module})].entity[(${subModule}?'.'+${subModule}:'')].[(${entity})]Entity; + +/** + * [(${name})] + */ +public interface [(${entity})]Mapper extends BaseMapper<[(${entity})]Entity> { +} diff --git a/cool-admin-java/src/main/resources/cool/code/service/impl.th b/cool-admin-java/src/main/resources/cool/code/service/impl.th new file mode 100644 index 0000000..1c31ea7 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/code/service/impl.th @@ -0,0 +1,14 @@ +package com.cool.modules.[(${module})].service[(${subModule}?'.'+${subModule}:'')].impl; + +import com.cool.core.base.BaseServiceImpl; +import com.cool.modules.[(${module})].entity[(${subModule}?'.'+${subModule}:'')].[(${entity})]Entity; +import com.cool.modules.[(${module})].mapper[(${subModule}?'.'+${subModule}:'')].[(${entity})]Mapper; +import com.cool.modules.[(${module})].service[(${subModule}?'.'+${subModule}:'')].[(${entity})]Service; +import org.springframework.stereotype.Service; + +/** + * [(${name})] + */ +@Service +public class [(${entity})]ServiceImpl extends BaseServiceImpl<[(${entity})]Mapper, [(${entity})]Entity> implements [(${entity})]Service { +} \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/cool/code/service/interface.th b/cool-admin-java/src/main/resources/cool/code/service/interface.th new file mode 100644 index 0000000..6b365b0 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/code/service/interface.th @@ -0,0 +1,10 @@ +package com.cool.modules.[(${module})].service[(${subModule}?'.'+${subModule}:'')]; + +import com.cool.core.base.BaseService; +import com.cool.modules.[(${module})].entity[(${subModule}?'.'+${subModule}:'')].[(${entity})]Entity; + +/** + * [(${name})] + */ +public interface [(${entity})]Service extends BaseService<[(${entity})]Entity> { +} diff --git a/cool-admin-java/src/main/resources/cool/data/db/base.json b/cool-admin-java/src/main/resources/cool/data/db/base.json new file mode 100644 index 0000000..5698188 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/data/db/base.json @@ -0,0 +1,121 @@ +{ + "base_sys_param": [ + { + "keyName": "rich", + "name": "富文本参数", + "data": "

这是一个富文本

xxx

xxxxxxxxxx


", + "dataType": 1, + "remark": null + }, + { + "keyName": "json", + "name": "JSON参数", + "data": "{\n \"code\": 111233\n}", + "dataType": 0, + "remark": null + }, + { + "keyName": "file", + "name": "文件", + "data": "", + "dataType": 2, + "remark": null + }, + { + "keyName": "text", + "name": "测试", + "data": "这是一段字符串", + "dataType": 0, + "remark": null + } + ], + "base_sys_conf": [ + { + "cKey": "logKeep", + "cValue": "31" + }, + { + "cKey": "recycleKeep", + "cValue": "31" + } + ], + "base_sys_department": [ + { + "id": 1, + "name": "COOL", + "parentId": null, + "orderNum": 0 + }, + { + "id": 11, + "name": "开发", + "parentId": 12, + "orderNum": 2 + }, + { + "id": 12, + "name": "测试", + "parentId": 1, + "orderNum": 1 + }, + { + "id": 13, + "name": "游客", + "parentId": 1, + "orderNum": 3 + } + ], + "base_sys_role": [ + { + "id": 1, + "userId": "1", + "name": "超管", + "label": "admin", + "remark": "最高权限的角色", + "relevance": 1, + "menuIdList": null, + "departmentIdList": null + } + ], + "base_sys_user": [ + { + "id": 1, + "departmentId": 1, + "name": "超级管理员", + "username": "admin", + "password": "e10adc3949ba59abbe56e057f20f883e", + "passwordV": 7, + "nickName": "管理员", + "headImg": "https://cool-js.com/admin/headimg.jpg", + "phone": "18000000000", + "email": "team@cool-js.com", + "status": 1, + "remark": "拥有最高权限的用户", + "socketId": null + } + ], + "base_sys_user_role": [ + { + "userId": 1, + "roleId": 1 + } + ], + "task_info": [ + { + "id": 1, + "name": "清理日志", + "job_id": "1", + "repeat_count": null, + "every": 5000, + "status": 1, + "service": "baseSysLogServiceImpl.clear(false)", + "task_type": 0, + "type": null, + "data": null, + "remark": null, + "cron": "0 0 1 * * ?", + "start_date": null, + "end_date": null + } + ] +} \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/cool/data/menu/menu.json b/cool-admin-java/src/main/resources/cool/data/menu/menu.json new file mode 100644 index 0000000..5da8568 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/data/menu/menu.json @@ -0,0 +1,813 @@ +[ + { + "name": "系统管理", + "router": "/sys", + "perms": null, + "type": 0, + "icon": "icon-set", + "orderNum": 2, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "权限管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-auth", + "orderNum": 1, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [ + { + "name": "菜单列表", + "router": "/sys/menu", + "perms": null, + "type": 1, + "icon": "icon-menu", + "orderNum": 2, + "viewPath": "modules/base/views/menu/index.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "新增", + "router": null, + "perms": "base:sys:menu:add", + "type": 2, + "icon": null, + "orderNum": 1, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "删除", + "router": null, + "perms": "base:sys:menu:delete", + "type": 2, + "icon": null, + "orderNum": 2, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "查询", + "router": null, + "perms": "base:sys:menu:page,base:sys:menu:list,base:sys:menu:info", + "type": 2, + "icon": null, + "orderNum": 4, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "参数", + "router": "/test/aa", + "perms": null, + "type": 1, + "icon": "icon-goods", + "orderNum": 0, + "viewPath": "modules/base/views/info.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "编辑", + "router": null, + "perms": "base:sys:menu:info,base:sys:menu:update", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "角色列表", + "router": "/sys/role", + "perms": null, + "type": 1, + "icon": "icon-dept", + "orderNum": 3, + "viewPath": "cool/modules/base/views/role.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "新增", + "router": null, + "perms": "base:sys:role:add", + "type": 2, + "icon": null, + "orderNum": 1, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "删除", + "router": null, + "perms": "base:sys:role:delete", + "type": 2, + "icon": null, + "orderNum": 2, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "修改", + "router": null, + "perms": "base:sys:role:update", + "type": 2, + "icon": null, + "orderNum": 3, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + }, + { + "name": "查询", + "router": null, + "perms": "base:sys:role:page,base:sys:role:list,base:sys:role:info", + "type": 2, + "icon": null, + "orderNum": 4, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "用户列表", + "router": "/sys/user", + "perms": null, + "type": 1, + "icon": "icon-user", + "orderNum": 0, + "viewPath": "modules/base/views/user/index.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "部门列表", + "router": null, + "perms": "base:sys:department:list", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "新增部门", + "router": null, + "perms": "base:sys:department:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "更新部门", + "router": null, + "perms": "base:sys:department:update", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "删除部门", + "router": null, + "perms": "base:sys:department:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "部门排序", + "router": null, + "perms": "base:sys:department:order", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "用户转移", + "router": null, + "perms": "base:sys:user:move", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "新增", + "router": null, + "perms": "base:sys:user:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "删除", + "router": null, + "perms": "base:sys:user:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "修改", + "router": null, + "perms": "base:sys:user:delete,base:sys:user:update", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "查询", + "router": null, + "perms": "base:sys:user:page,base:sys:user:list,base:sys:user:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + }, + { + "name": "参数配置", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-params", + "orderNum": 3, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "参数列表", + "router": "/sys/param", + "perms": null, + "type": 1, + "icon": "icon-menu", + "orderNum": 0, + "viewPath": "cool/modules/base/views/param.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "新增", + "router": null, + "perms": "base:sys:param:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "修改", + "router": null, + "perms": "base:sys:param:info,base:sys:param:update", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "删除", + "router": null, + "perms": "base:sys:param:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "查看", + "router": null, + "perms": "base:sys:param:page,base:sys:param:list,base:sys:param:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + }, + { + "name": "监控管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-monitor", + "orderNum": 9, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "请求日志", + "router": "/sys/log", + "perms": null, + "type": 1, + "icon": "icon-log", + "orderNum": 1, + "viewPath": "cool/modules/base/views/log.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "权限", + "router": null, + "perms": "base:sys:log:page,base:sys:log:clear,base:sys:log:getKeep,base:sys:log:setKeep", + "type": 2, + "icon": null, + "orderNum": 1, + "viewPath": null, + "keepAlive": false, + "isShow": true, + "childMenus": [] + } + ] + } + ] + }, + { + "name": "任务管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-activity", + "orderNum": 9, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "任务列表", + "router": "/task/list", + "perms": null, + "type": 1, + "icon": "icon-menu", + "orderNum": 0, + "viewPath": "modules/task/views/list.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "权限", + "router": null, + "perms": "task:info:page,task:info:list,task:info:info,task:info:add,task:info:delete,task:info:update,task:info:stop,task:info:start,task:info:once,task:info:log", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + } + ] + }, + { + "name": "框架教程", + "router": "/tutorial", + "perms": null, + "type": 0, + "icon": "icon-task", + "orderNum": 98, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "文档官网", + "router": "/tutorial/doc", + "perms": null, + "type": 1, + "icon": "icon-log", + "orderNum": 0, + "viewPath": "https://admin.cool-js.com", + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "crud 示例", + "router": "/demo/crud", + "perms": null, + "type": 1, + "icon": "icon-favor", + "orderNum": 1, + "viewPath": "modules/demo/views/crud/index.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "通用", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-radioboxfill", + "orderNum": 99, + "viewPath": null, + "keepAlive": true, + "isShow": false, + "childMenus": [ + { + "name": "图片上传", + "router": null, + "perms": "space:info:page,space:info:list,space:info:info,space:info:add,space:info:delete,space:info:update,space:type:page,space:type:list,space:type:info,space:type:add,space:type:delete,space:type:update", + "type": 2, + "icon": null, + "orderNum": 1, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "首页", + "router": "/", + "perms": null, + "type": 1, + "icon": null, + "orderNum": 0, + "viewPath": "modules/demo/views/home/index.vue", + "keepAlive": true, + "isShow": false, + "childMenus": [] + }, + { + "name": "数据管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-data", + "orderNum": 7, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "字典管理", + "router": "/dict/list", + "perms": null, + "type": 1, + "icon": "icon-dict", + "orderNum": 3, + "viewPath": "modules/dict/views/list.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "删除", + "router": null, + "perms": "dict:info:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "修改", + "router": null, + "perms": "dict:info:update,dict:info:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "获得字典数据", + "router": null, + "perms": "dict:info:data", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "单个信息", + "router": null, + "perms": "dict:info:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "列表查询", + "router": null, + "perms": "dict:info:list", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "分页查询", + "router": null, + "perms": "dict:info:page", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "新增", + "router": null, + "perms": "dict:info:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "组权限", + "router": null, + "perms": "dict:type:list,dict:type:update,dict:type:delete,dict:type:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "数据回收站", + "router": "/recycle/data", + "perms": null, + "type": 1, + "icon": "icon-delete", + "orderNum": 6, + "viewPath": "modules/recycle/views/data.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "恢复数据", + "router": null, + "perms": "recycle:data:restore", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "单个信息", + "router": null, + "perms": "recycle:data:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "分页查询", + "router": null, + "perms": "recycle:data:page", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + }, + { + "name": "文件管理", + "router": "/upload/list", + "perms": null, + "type": 1, + "icon": "icon-log", + "orderNum": 5, + "viewPath": "modules/space/views/list.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "权限", + "router": null, + "perms": "space:type:delete,space:type:update,space:type:info,space:type:list,space:type:page,space:type:add,space:info:getConfig,space:info:delete,space:info:update,space:info:info,space:info:list,space:info:page,space:info:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + }, + { + "name": "用户管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-user", + "orderNum": 11, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "用户列表", + "router": "/user/list", + "perms": null, + "type": 1, + "icon": "icon-menu", + "orderNum": 1, + "viewPath": "modules/user/views/list.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "删除", + "router": null, + "perms": "user:info:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "修改", + "router": null, + "perms": "user:info:update,user:info:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "单个信息", + "router": null, + "perms": "user:info:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "列表查询", + "router": null, + "perms": "user:info:list", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "分页查询", + "router": null, + "perms": "user:info:page", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "name": "新增", + "router": null, + "perms": "user:info:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + } +] \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/cool/data/menu/menu_8.0.json b/cool-admin-java/src/main/resources/cool/data/menu/menu_8.0.json new file mode 100644 index 0000000..4ad1c60 --- /dev/null +++ b/cool-admin-java/src/main/resources/cool/data/menu/menu_8.0.json @@ -0,0 +1,158 @@ +[ + { + "name": "数据管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-data", + "orderNum": 7, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "字典管理", + "router": "/dict/list", + "perms": null, + "type": 1, + "icon": "icon-dict", + "orderNum": 3, + "viewPath": "modules/dict/views/list.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "name": "字典类型", + "router": null, + "perms": "dict:type:delete,dict:type:update,dict:type:info,dict:type:list,dict:type:page,dict:type:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + ] + } + ] + }] + }, + { + "name": "扩展管理", + "router": null, + "perms": null, + "type": 0, + "icon": "icon-favor", + "orderNum": 8, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "tenantId": null, + "name": "插件列表", + "router": "/helper/plugins", + "perms": null, + "type": 1, + "icon": "icon-list", + "orderNum": 1, + "viewPath": "modules/helper/views/plugins.vue", + "keepAlive": true, + "isShow": true, + "childMenus": [ + { + "tenantId": null, + "name": "删除", + "router": null, + "perms": "plugin:info:delete", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "分页查询", + "router": null, + "perms": "plugin:info:page", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "单个信息", + "router": null, + "perms": "plugin:info:info", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "安装插件", + "router": null, + "perms": "plugin:info:install", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "修改", + "router": null, + "perms": "plugin:info:update", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "列表查询", + "router": null, + "perms": "plugin:info:list", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + }, + { + "tenantId": null, + "name": "新增", + "router": null, + "perms": "plugin:info:add", + "type": 2, + "icon": null, + "orderNum": 0, + "viewPath": null, + "keepAlive": true, + "isShow": true, + "childMenus": [] + } + ] + } + ] + } +] \ No newline at end of file diff --git a/cool-admin-java/src/main/resources/static/css/welcome.css b/cool-admin-java/src/main/resources/static/css/welcome.css new file mode 100644 index 0000000..c838986 --- /dev/null +++ b/cool-admin-java/src/main/resources/static/css/welcome.css @@ -0,0 +1,89 @@ +body { + display: flex; + height: 100vh; + justify-content: center; + align-items: center; + text-align: center; + background: #222; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.footer-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + color: #6ee1f5; + padding: 10px 0 20px 0; + text-align: center; + opacity: 0; /* 开始时隐藏 */ + animation: fadeIn 5s forwards; /* 应用动画 */ +} + +.link { + color: #6ee1f5; +} + +.reveal { + position: relative; + display: flex; + color: #6ee1f5; + font-size: 2em; + font-family: Raleway, sans-serif; + letter-spacing: 3px; + text-transform: uppercase; + white-space: pre; +} +.reveal span { + opacity: 0; + transform: scale(0); + animation: fadeIn 2.4s forwards; +} +.reveal::before, .reveal::after { + position: absolute; + content: ""; + top: 0; + bottom: 0; + width: 2px; + height: 100%; + background: white; + opacity: 0; + transform: scale(0); +} +.reveal::before { + left: 50%; + animation: slideLeft 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards; +} +.reveal::after { + right: 50%; + animation: slideRight 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards; +} + +@keyframes fadeIn { + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes slideLeft { + to { + left: -6%; + opacity: 1; + transform: scale(0.9); + } +} +@keyframes slideRight { + to { + right: -6%; + opacity: 1; + transform: scale(0.9); + } +} diff --git a/cool-admin-java/src/main/resources/static/favicon.ico b/cool-admin-java/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..c4d673d Binary files /dev/null and b/cool-admin-java/src/main/resources/static/favicon.ico differ diff --git a/cool-admin-java/src/main/resources/static/js/welcome.js b/cool-admin-java/src/main/resources/static/js/welcome.js new file mode 100644 index 0000000..eb9b697 --- /dev/null +++ b/cool-admin-java/src/main/resources/static/js/welcome.js @@ -0,0 +1,14 @@ +const duration = 0.8; +const delay = 0.3; +// eslint-disable-next-line no-undef +const revealText = document.querySelector('.reveal'); +const letters = revealText.textContent.split(''); +revealText.textContent = ''; +const middle = letters.filter(e => e !== ' ').length / 2; +letters.forEach((letter, i) => { + // eslint-disable-next-line no-undef + const span = document.createElement('span'); + span.textContent = letter; + span.style.animationDelay = `${delay + Math.abs(i - middle) * 0.1}s`; + revealText.append(span); +}); diff --git a/cool-admin-java/src/main/resources/templates/welcome.html b/cool-admin-java/src/main/resources/templates/welcome.html new file mode 100644 index 0000000..2bd2140 --- /dev/null +++ b/cool-admin-java/src/main/resources/templates/welcome.html @@ -0,0 +1,25 @@ + + + + + + + + COOL-ADMIN 一个很酷的后台管理系统开发框架 + + + + + +
HELLO COOL-ADMIN V8.x
+ + + + + + diff --git a/cool-admin-java/src/test/java/com/cool/CoolAdminJavaApplicationTests.java b/cool-admin-java/src/test/java/com/cool/CoolAdminJavaApplicationTests.java new file mode 100644 index 0000000..1d280e5 --- /dev/null +++ b/cool-admin-java/src/test/java/com/cool/CoolAdminJavaApplicationTests.java @@ -0,0 +1,13 @@ +package com.cool; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class CoolAdminJavaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/cool-admin-java/src/test/java/com/cool/CoolCodeGeneratorTest.java b/cool-admin-java/src/test/java/com/cool/CoolCodeGeneratorTest.java new file mode 100644 index 0000000..acf8caf --- /dev/null +++ b/cool-admin-java/src/test/java/com/cool/CoolCodeGeneratorTest.java @@ -0,0 +1,56 @@ +package com.cool; + +import com.cool.core.code.CodeGenerator; +import com.cool.core.code.CodeModel; +import com.cool.core.code.CodeTypeEnum; +import com.cool.modules.user.entity.*; +import com.mybatisflex.annotation.Table; +import java.util.List; + +public class CoolCodeGeneratorTest { + public static void main(String[] args) { + CodeGenerator codeGenerator = new CodeGenerator(); + codeGenerator.init(); + List list = List.of(UserWxEntity.class); + + list.forEach(o -> { + Table annotation = (Table) o.getAnnotation(Table.class); + CodeModel codeModel = new CodeModel(); + codeModel.setType(CodeTypeEnum.APP); + codeModel.setName(annotation.comment()); + codeModel.setModule(getFirstWord(o.getSimpleName())); + codeModel.setEntity(o); + // 生成 controller +// codeGenerator.controller(codeModel); + // 生成 mapper + codeGenerator.mapper(codeModel); + // 生成 service + codeGenerator.service(codeModel); + }); + } + + public static String getFirstWord(String className) { + if (className == null || className.isEmpty()) { + return ""; + } + + StringBuilder firstWord = new StringBuilder(); + boolean foundFirstWord = false; + + for (char c : className.toCharArray()) { + if (Character.isUpperCase(c)) { + if (foundFirstWord) { + break; + } + firstWord.append(c); + foundFirstWord = true; + } else { + if (foundFirstWord) { + firstWord.append(c); + } + } + } + + return firstWord.toString().toLowerCase(); + } +} diff --git a/cool-admin-vue/.cursorrules b/cool-admin-vue/.cursorrules new file mode 100644 index 0000000..c0df244 --- /dev/null +++ b/cool-admin-vue/.cursorrules @@ -0,0 +1,74 @@ +# 项目背景 + +- 库:typescript、javaScript、scss、vue、tailwind +- 框架:cool-admin-vue +- 项目版本:8.x + +# 项目目录 + +├── .vscode(代码片段,根据关键字可以快速地生成代码) +├── public(静态资源文件) +├── packages(源码包:@cool-vue/crud、@cool-vue/vite-plugin) +├── build +│ └── cool() +│ │ └── eps.json(Eps 配置文件) +│ │ └── eps.d.ts(Eps 描述文件) +├── src +│ └── cool(核心文件) +│ └── modules(项目模块) +│ │ └── base(基础模块) +│ │ └── demo(示例模块) +│ │ └── dict(字典模块) +│ │ └── helper(辅助模块) +│ │ └── recycle(回收站模块) +│ │ └── space(cl-upload-space 文件空间模块) +│ │ └── task(任务模块) +│ │ └── user(用户模块) +│ └── plugins(项目插件) +│ │ └── crud(cl-crud、@cool-vue/crud) +│ │ └── distpicker(cl-distpicker、省市区选择器) +│ │ └── echarts(图标) +│ │ └── editor-preview(编辑器预览组件) +│ │ └── editor-wange(wang富文本编辑器) +│ │ └── element-ui(element-plus 组件) +│ │ └── excel(excel导入、导出组件) +│ │ └── i18n(多语言) +│ │ └── iconfont(iconfont 图标) +│ │ └── theme(cl-theme 主题组件) +│ │ └── upload(cl-upload 文件上传组件) +│ │ └── view(cl-view-group、cl-view-head 视图组件) +│ └── config +│ │ └── index.ts(默认配置) +│ │ └── dev.ts(开发环境) +│ │ └── prod.ts(生产环境) +│ │ └── proxy.ts(代理配置) +│ └── App.vue(入口文件) +│ └── main.ts(入口文件) +├── package.json(依赖管理,项目信息) +└── ... + +模块、插件目录 +├── modules/plugins +│ └── base(模块名) +│ │ └── components(全局组件) +│ │ └── directives(全局指令) +│ │ └── locales(国际化) +│ │ └── router(路由) +│ │ └── store(状态管理) +│ │ └── utils(工具函数) +│ │ └── views(视图) +│ │ └── config.ts(必须,模块的配置) +│ │ └── index.ts(模块导出) + +# 其它 + +- 文件、组件命名用 - 连接,如:student-info.vue +- service 的描述类型,查看 build/cool/eps.d.ts 描述文件 +- 创建模块、插件代码需要读取.cursor/rules的module.mdc,其它的rules根据需要进行参考 + +# import 引用别名 + +- "/@" 对应 "./src" +- "/$" 对应 "./src/modules" +- "/#" 对应 "./src/plugins" +- "/~" 对应 "./packages" diff --git a/cool-admin-vue/.dockerignore b/cool-admin-vue/.dockerignore new file mode 100644 index 0000000..d451ff1 --- /dev/null +++ b/cool-admin-vue/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/cool-admin-vue/.editorconfig b/cool-admin-vue/.editorconfig new file mode 100644 index 0000000..284364f --- /dev/null +++ b/cool-admin-vue/.editorconfig @@ -0,0 +1,7 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] +charset = utf-8 +end_of_line = lf +indent_style = tab +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/cool-admin-vue/.env b/cool-admin-vue/.env new file mode 100644 index 0000000..028356d --- /dev/null +++ b/cool-admin-vue/.env @@ -0,0 +1,5 @@ +# 应用名称 +VITE_NAME = "COOL-ADMIN" + +# 网络超时请求时间 +VITE_TIMEOUT = 30000 \ No newline at end of file diff --git a/cool-admin-vue/.gitattributes b/cool-admin-vue/.gitattributes new file mode 100644 index 0000000..e41187f --- /dev/null +++ b/cool-admin-vue/.gitattributes @@ -0,0 +1,4 @@ +*.js text eol=lf +*.json text eol=lf +*.ts text eol=lf +*.vue text eol=lf \ No newline at end of file diff --git a/cool-admin-vue/.gitignore b/cool-admin-vue/.gitignore new file mode 100644 index 0000000..615a016 --- /dev/null +++ b/cool-admin-vue/.gitignore @@ -0,0 +1,20 @@ +.DS_Store +node_modules/ +/dist/ +dist-ssr/ + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.project +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw* + +vite.config.ts.timestamp* diff --git a/cool-admin-vue/.hintrc b/cool-admin-vue/.hintrc new file mode 100644 index 0000000..de024c3 --- /dev/null +++ b/cool-admin-vue/.hintrc @@ -0,0 +1,16 @@ +{ + "extends": [ + "development" + ], + "hints": { + "meta-viewport": "off", + "axe/text-alternatives": [ + "default", + { + "document-title": "off" + } + ], + "disown-opener": "off", + "css-prefix-order": "off" + } +} \ No newline at end of file diff --git a/cool-admin-vue/.prettierrc.json b/cool-admin-vue/.prettierrc.json new file mode 100644 index 0000000..29d7d66 --- /dev/null +++ b/cool-admin-vue/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": true, + "useTabs": true, + "tabWidth": 4, + "printWidth": 100, + "singleQuote": true, + "arrowParens": "avoid", + "trailingComma": "none" +} diff --git a/cool-admin-vue/Dockerfile b/cool-admin-vue/Dockerfile new file mode 100644 index 0000000..811db32 --- /dev/null +++ b/cool-admin-vue/Dockerfile @@ -0,0 +1,14 @@ +FROM node:lts-alpine +WORKDIR /build +# 设置npm镜像 +RUN npm config set registry https://registry.npmmirror.com +COPY package.json /build/package.json +RUN npm install +COPY ./ /build +RUN npm run build + +FROM nginx +RUN mkdir /app +COPY --from=0 /build/dist /app +COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf +EXPOSE 80 diff --git a/cool-admin-vue/LICENSE b/cool-admin-vue/LICENSE new file mode 100644 index 0000000..b6722d4 --- /dev/null +++ b/cool-admin-vue/LICENSE @@ -0,0 +1,33 @@ +MIT License + +Copyright (c) [2025] [厦门闪酷科技开发有限公司] + +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. + +--- + +MIT 许可证 + +版权所有 (c) [2025] [厦门闪酷科技开发有限公司] + +特此免费授予获得本软件及相关文档文件(“软件”)副本的任何人无限制地处理本软件的权限,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件的副本,并允许软件提供给其的人员这样做,但须符合以下条件: + +上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。 + +本软件按“原样”提供,不提供任何明示或暗示的担保,包括但不限于对适销性、特定用途适用性和非侵权的担保。在任何情况下,作者或版权持有人均不对因软件或软件使用或其他交易而产生的任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权诉讼或其他诉讼中。 \ No newline at end of file diff --git a/cool-admin-vue/README.md b/cool-admin-vue/README.md new file mode 100644 index 0000000..7a03692 --- /dev/null +++ b/cool-admin-vue/README.md @@ -0,0 +1,78 @@ +# cool-admin [vue3 - ts - vite] + +

+ cool-admin Logo +

+ +

cool-admin 一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发 CRUD,方便快速构建迭代后台管理系统, 到文档 进一步了解

+ +

+ GitHub license + GitHub tag + GitHub tag +

+ +## 特性 + +Ai时代,很多老旧的框架已经无法满足现代化的开发需求,Cool-Admin开发了一系列的功能,让开发变得更简单、更快速、更高效。 + +- **Ai编码**:通过微调大模型学习框架特有写法,实现简单功能从Api接口到前端页面的一键生成 +- **流程编排**:通过拖拽编排方式,即可实现类似像智能客服这样的功能 +- **模块化**:代码是模块化的,清晰明了,方便维护 +- **插件化**:插件化的设计,可以通过安装插件的方式扩展如:支付、短信、邮件等功能 + +![](https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/flow.png) + +## 地址 + +- [📌 v7 版本](https://github.com/cool-team-official/cool-admin-vue/tree/7.x) + +- [🌐 码云仓库](https://gitee.com/cool-team-official/cool-admin-vue) + +## 视频教程 + +[官方 B 站视频教程](https://www.bilibili.com/video/BV1j1421R7aB) + +## 演示 + +[https://show.cool-admin.com](https://show.cool-admin.com) + +账户:admin,密码:123456 + +Admin Home + +## 项目后端 + +[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway) + +或 + +[https://gitee.com/cool-team-official/cool-admin-midway](https://gitee.com/cool-team-official/cool-admin-midway) + +或 + +[https://gitcode.com/cool_team/cool-admin-midway](https://gitcode.com/cool_team/cool-admin-midway) + +## 微信群 + +Admin Wechat + +## 安装项目依赖 + +推荐使用 `pnpm`: + +```shell +pnpm i +``` + +## 运行应用程序 + +安装过程完成后,运行以下命令启动服务。您可以在浏览器中预览网站 [http://localhost:9000](http://localhost:9000) + +```shell +pnpm dev +``` + +### 低价服务器 + +[阿里云、腾讯云、华为云低价云服务器,不限新老](https://cool-js.com/service/cloud) diff --git a/cool-admin-vue/env.d.ts b/cool-admin-vue/env.d.ts new file mode 100644 index 0000000..b5a1827 --- /dev/null +++ b/cool-admin-vue/env.d.ts @@ -0,0 +1,10 @@ +/// + +interface ImportMetaEnv { + readonly VITE_NAME: string; + readonly VITE_TIMEOUT: number; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/cool-admin-vue/eslint.config.js b/cool-admin-vue/eslint.config.js new file mode 100644 index 0000000..a361065 --- /dev/null +++ b/cool-admin-vue/eslint.config.js @@ -0,0 +1,73 @@ +import pluginVue from 'eslint-plugin-vue'; +import vueTsEslintConfig from '@vue/eslint-config-typescript'; +import prettier from 'eslint-plugin-prettier'; +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'; + +export default [ + { + name: 'app/files-to-lint', + files: ['**/*.{ts,mts,tsx,vue}'], + }, + + { + name: 'app/files-to-ignore', + ignores: [ + '**/dist/**', + '**/dist-ssr/**', + '**/coverage/**', + '**/packages/**', + '**/build/**', + ], + }, + + ...pluginVue.configs['flat/recommended'], + ...vueTsEslintConfig(), + skipFormatting, + + { + languageOptions: { + parserOptions: { + ecmaVersion: 2020, + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + '@typescript-eslint/ban-ts-ignore': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + 'space-before-function-paren': 'off', + 'no-unused-vars': 'off', + 'no-use-before-define': 'off', + 'no-self-assign': 'off', + 'vue/no-mutating-props': 'off', + 'vue/no-template-shadow': 'off', + 'vue/no-v-html': 'off', + 'vue/component-name-in-template-casing': ['error', 'kebab-case'], + 'vue/component-definition-name-casing': ['error', 'kebab-case'], + 'vue/attributes-order': 'off', + 'vue/one-component-per-file': 'off', + 'vue/html-closing-bracket-newline': 'off', + 'vue/max-attributes-per-line': 'off', + 'vue/multiline-html-element-content-newline': 'off', + 'vue/multi-word-component-names': 'off', + 'vue/singleline-html-element-content-newline': 'off', + 'vue/attribute-hyphenation': 'off', + 'vue/html-self-closing': 'off', + 'vue/require-default-prop': 'off', + 'vue/v-on-event-hyphenation': 'off', + 'vue/block-lang': 'off', + }, + }, +]; diff --git a/cool-admin-vue/index.html b/cool-admin-vue/index.html new file mode 100644 index 0000000..1148b64 --- /dev/null +++ b/cool-admin-vue/index.html @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ +
+ + + diff --git a/cool-admin-vue/nginx.conf b/cool-admin-vue/nginx.conf new file mode 100644 index 0000000..2a02b0a --- /dev/null +++ b/cool-admin-vue/nginx.conf @@ -0,0 +1,72 @@ +user nginx; +worker_processes 1; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; +events { + worker_connections 1024; +} +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + upstream cool { + server midway:8001; + } + + server { + listen 80; + server_name localhost; + location / { + root /app; + index index.html; + try_files $uri $uri/ /index.html; + } + location /api/ { + proxy_pass http://cool/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header REMOTE-HOST $remote_addr; + + #缓存相关配置 + #proxy_cache cache_one; + #proxy_cache_key $host$request_uri$is_args$args; + #proxy_cache_valid 200 304 301 302 1h; + + #持久化连接相关配置 + proxy_connect_timeout 3000s; + proxy_read_timeout 86400s; + proxy_send_timeout 3000s; + #proxy_http_version 1.1; + #proxy_set_header Upgrade $http_upgrade; + #proxy_set_header Connection "upgrade"; + + add_header X-Cache $upstream_cache_status; + + #expires 12h; + } + + # socket需额外配置 + location /socket { + proxy_pass http://cool/socket; + proxy_connect_timeout 3600s; #配置点1 + proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点 + proxy_send_timeout 3600s; #配置点3 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header REMOTE-HOST $remote_addr; + #proxy_bind $remote_addr transparent; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + rewrite /socket/(.*) /$1 break; + proxy_redirect off; + } + } +} \ No newline at end of file diff --git a/cool-admin-vue/package-lock.json b/cool-admin-vue/package-lock.json new file mode 100644 index 0000000..80ea03c --- /dev/null +++ b/cool-admin-vue/package-lock.json @@ -0,0 +1,8907 @@ +{ + "name": "cool-admin-vue", + "version": "8.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cool-admin-vue", + "version": "8.0.0", + "dependencies": { + "@cool-vue/crud": "^8.0.6", + "@element-plus/icons-vue": "^2.3.1", + "@vueuse/core": "^12.5.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "axios": "^1.7.9", + "chardet": "^2.0.0", + "core-js": "^3.40.0", + "dayjs": "^1.11.13", + "echarts": "^5.6.0", + "element-plus": "2.10.2", + "file-saver": "^2.0.5", + "lodash-es": "^4.17.21", + "marked": "^14.1.3", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", + "pinia": "^2.3.1", + "store": "^2.0.12", + "vue": "^3.5.13", + "vue-echarts": "^7.0.3", + "vue-i18n": "^11.0.1", + "vue-router": "^4.5.0", + "vuedraggable": "^4.1.0", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@cool-vue/vite-plugin": "^8.2.2", + "@intlify/unplugin-vue-i18n": "^6.0.3", + "@rushstack/eslint-patch": "^1.10.5", + "@tsconfig/node20": "^20.1.4", + "@types/file-saver": "^2.0.7", + "@types/lodash-es": "^4.17.12", + "@types/mockjs": "^1.0.10", + "@types/node": "^20.17.17", + "@types/nprogress": "^0.2.3", + "@types/store": "^2.0.5", + "@vitejs/plugin-vue": "^5.2.1", + "@vitejs/plugin-vue-jsx": "^4.1.1", + "@vue/compiler-sfc": "^3.5.13", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.3.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.5.1", + "autoprefixer": "^10.4.20", + "eslint": "^9.19.0", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-vue": "^9.32.0", + "postcss": "^8.5.1", + "prettier": "^3.4.2", + "rollup-plugin-visualizer": "^5.14.0", + "sass": "1.81.0", + "tailwindcss": "^3.4.17", + "terser": "^5.36.0", + "typescript": "~5.5.4", + "vite": "^5.4.14", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-vue-devtools": "^7.7.1", + "vue-tsc": "^2.2.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cool-vue/crud": { + "version": "8.0.6", + "resolved": "https://registry.npmmirror.com/@cool-vue/crud/-/crud-8.0.6.tgz", + "integrity": "sha512-be6nKaUhdRmr2OQD6kJS6C+d1QTf6ibkp5fRSqs3x+lDuZOnyCh4/f/xg6ShKh2vtf+Bidwc2ftuxyEWx3A8SA==", + "dependencies": { + "@vue/runtime-core": "^3.5.13", + "element-plus": "^2.10.4", + "lodash-es": "^4.17.21", + "vue": "^3.5.13" + } + }, + "node_modules/@cool-vue/crud/node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/@cool-vue/crud/node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@cool-vue/crud/node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@cool-vue/crud/node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@cool-vue/crud/node_modules/element-plus": { + "version": "2.10.7", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.7.tgz", + "integrity": "sha512-bL4yhepL8/0NEQA5+N2Q6ZVKLipIDkiQjK2mqtSmGh6CxJk1yaBMdG5HXfYkbk1htNcT3ULk9g23lzT323JGcA==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@cool-vue/vite-plugin": { + "version": "8.2.6", + "resolved": "https://registry.npmmirror.com/@cool-vue/vite-plugin/-/vite-plugin-8.2.6.tgz", + "integrity": "sha512-Ey1qIMoHZOZdl/PxZ7qSa5A8hah0IlMVoyxPH7PQk6tAB1PNJiUfTkrPFi9vohFEeuj3WMZ5NhlRh+UIC3ZRXw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@vue/compiler-sfc": "^3.5.13", + "axios": "^1.6.8", + "glob": "^10.3.12", + "lodash": "^4.17.21", + "magic-string": "^0.30.17", + "prettier": "^3.4.2", + "svgo": "^3.3.2" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.33.0", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@intlify/bundle-utils": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-10.0.1.tgz", + "integrity": "sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "^11.1.2", + "@intlify/shared": "^11.1.2", + "acorn": "^8.8.2", + "escodegen": "^2.1.0", + "estree-walker": "^2.0.2", + "jsonc-eslint-parser": "^2.3.0", + "mlly": "^1.2.0", + "source-map-js": "^1.0.1", + "yaml-eslint-parser": "^1.2.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependenciesMeta": { + "petite-vue-i18n": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, + "node_modules/@intlify/core-base": { + "version": "11.1.11", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.1.11.tgz", + "integrity": "sha512-1Z0N8jTfkcD2Luq9HNZt+GmjpFe4/4PpZF3AOzoO1u5PTtSuXZcfhwBatywbfE2ieB/B5QHIoOFmCXY2jqVKEQ==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "11.1.11", + "@intlify/shared": "11.1.11" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.1.11", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.1.11.tgz", + "integrity": "sha512-7PC6neomoc/z7a8JRjPBbu0T2TzR2MQuY5kn2e049MP7+o32Ve7O8husylkA7K9fQRe4iNXZWTPnDJ6vZdtS1Q==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "11.1.11", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.1.11", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-11.1.11.tgz", + "integrity": "sha512-RIBFTIqxZSsxUqlcyoR7iiC632bq7kkOwYvZlvcVObHfrF4NhuKc4FKvu8iPCrEO+e3XsY7/UVpfgzg+M7ETzA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/unplugin-vue-i18n": { + "version": "6.0.8", + "resolved": "https://registry.npmmirror.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-6.0.8.tgz", + "integrity": "sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@intlify/bundle-utils": "^10.0.1", + "@intlify/shared": "^11.1.2", + "@intlify/vue-i18n-extensions": "^8.0.0", + "@rollup/pluginutils": "^5.1.0", + "@typescript-eslint/scope-manager": "^8.13.0", + "@typescript-eslint/typescript-estree": "^8.13.0", + "debug": "^4.3.3", + "fast-glob": "^3.2.12", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "pathe": "^1.0.0", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2", + "unplugin": "^1.1.0", + "vue": "^3.4" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "petite-vue-i18n": "*", + "vue": "^3.2.25", + "vue-i18n": "*" + }, + "peerDependenciesMeta": { + "petite-vue-i18n": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, + "node_modules/@intlify/vue-i18n-extensions": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz", + "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.24.6", + "@intlify/shared": "^10.0.0", + "@vue/compiler-dom": "^3.2.45", + "vue-i18n": "^10.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0", + "@vue/compiler-dom": "^3.0.0", + "vue": "^3.0.0", + "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0" + }, + "peerDependenciesMeta": { + "@intlify/shared": { + "optional": true + }, + "@vue/compiler-dom": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, + "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/core-base": { + "version": "10.0.8", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-10.0.8.tgz", + "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "10.0.8", + "@intlify/shared": "10.0.8" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/message-compiler": { + "version": "10.0.8", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-10.0.8.tgz", + "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/shared": "10.0.8", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": { + "version": "10.0.8", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-10.0.8.tgz", + "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": { + "version": "10.0.8", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-10.0.8.tgz", + "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/core-base": "10.0.8", + "@intlify/shared": "10.0.8", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.32", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", + "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.12.0", + "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.0.7", + "resolved": "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", + "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==", + "license": "MIT" + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.6", + "resolved": "https://registry.npmmirror.com/@tsconfig/node20/-/node20-20.1.6.tgz", + "integrity": "sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/@types/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==", + "license": "MIT" + }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/mockjs": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/@types/mockjs/-/mockjs-1.0.10.tgz", + "integrity": "sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.11", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nprogress": { + "version": "0.2.3", + "resolved": "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.3.tgz", + "integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/store": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@types/store/-/store-2.0.5.tgz", + "integrity": "sha512-5NmTKe3GWdOaykzq7no+Ahf6mafJu0oLc9JNhJ3E26+0oFvd6GnksnZQpMXcH526mfG4xDYjFiKzyDL51PzeWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.39.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@uppy/companion-client": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/@uppy/companion-client/-/companion-client-2.2.2.tgz", + "integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==", + "license": "MIT", + "dependencies": { + "@uppy/utils": "^4.1.2", + "namespace-emitter": "^2.0.1" + } + }, + "node_modules/@uppy/core": { + "version": "2.3.4", + "resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz", + "integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==", + "license": "MIT", + "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.1.25", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/store-default": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/@uppy/store-default/-/store-default-2.1.1.tgz", + "integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==", + "license": "MIT" + }, + "node_modules/@uppy/utils": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/@uppy/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==", + "license": "MIT", + "dependencies": { + "lodash.throttle": "^4.1.1" + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz", + "integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==", + "license": "MIT", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2", + "nanoid": "^3.1.25" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-4.2.0.tgz", + "integrity": "sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1", + "@rolldown/pluginutils": "^1.0.0-beta.9", + "@vue/babel-plugin-jsx": "^1.4.0" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-core": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-core/-/devtools-core-7.7.7.tgz", + "integrity": "sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7", + "@vue/devtools-shared": "^7.7.7", + "mitt": "^3.0.1", + "nanoid": "^5.1.0", + "pathe": "^2.0.3", + "vite-hot-client": "^2.0.4" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.5", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-core/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.6.0", + "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-14.6.0.tgz", + "integrity": "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.35.1", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.35.1", + "vue-eslint-parser": "^10.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "vue": "3.5.18" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmmirror.com/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/@vue/tsconfig": { + "version": "0.5.1", + "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@wangeditor/basic-modules": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz", + "integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==", + "license": "MIT", + "dependencies": { + "is-url": "^1.2.4" + }, + "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" + } + }, + "node_modules/@wangeditor/code-highlight": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz", + "integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.23.0" + }, + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/core": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/@wangeditor/core/-/core-1.1.19.tgz", + "integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==", + "license": "MIT", + "dependencies": { + "@types/event-emitter": "^0.3.3", + "event-emitter": "^0.3.5", + "html-void-elements": "^2.0.0", + "i18next": "^20.4.0", + "scroll-into-view-if-needed": "^2.2.28", + "slate-history": "^0.66.0" + }, + "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" + } + }, + "node_modules/@wangeditor/editor": { + "version": "5.1.23", + "resolved": "https://registry.npmmirror.com/@wangeditor/editor/-/editor-5.1.23.tgz", + "integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==", + "license": "MIT", + "dependencies": { + "@uppy/core": "^2.1.1", + "@uppy/xhr-upload": "^2.0.3", + "@wangeditor/basic-modules": "^1.1.7", + "@wangeditor/code-highlight": "^1.0.3", + "@wangeditor/core": "^1.1.19", + "@wangeditor/list-module": "^1.0.5", + "@wangeditor/table-module": "^1.1.4", + "@wangeditor/upload-image-module": "^1.0.2", + "@wangeditor/video-module": "^1.1.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.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/editor-for-vue": { + "version": "5.1.12", + "resolved": "https://registry.npmmirror.com/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz", + "integrity": "sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/editor": ">=5.1.0", + "vue": "^3.0.5" + } + }, + "node_modules/@wangeditor/list-module": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@wangeditor/list-module/-/list-module-1.0.5.tgz", + "integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/table-module": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@wangeditor/table-module/-/table-module-1.1.4.tgz", + "integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==", + "license": "MIT", + "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" + } + }, + "node_modules/@wangeditor/upload-image-module": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz", + "integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==", + "license": "MIT", + "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" + } + }, + "node_modules/@wangeditor/video-module": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@wangeditor/video-module/-/video-module-1.1.4.tgz", + "integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==", + "license": "MIT", + "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" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001735", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-js": { + "version": "3.45.0", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.45.0.tgz", + "integrity": "sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom7": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz", + "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", + "license": "MIT", + "dependencies": { + "ssr-window": "^3.0.0-alpha.1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.202", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz", + "integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==", + "dev": true, + "license": "ISC" + }, + "node_modules/element-plus": { + "version": "2.10.2", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.2.tgz", + "integrity": "sha512-p2KiAa0jEGXrzdlTAfpiS7HQFAhla4gvx6H7RuDf+OO0uC3DGpolxvdHjFR8gt7+vaWyxQNcHa1sAdBkmjqlgA==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/element-plus/node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/element-plus/node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/element-plus/node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/element-plus/node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/error-stack-parser-es/-/error-stack-parser-es-0.1.5.tgz", + "integrity": "sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "9.33.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.33.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-vue/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-vue/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-vue/node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/i18next": { + "version": "20.6.1", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-20.6.1.tgz", + "integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hotkey": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz", + "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", + "license": "MIT" + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmmirror.com/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-eslint-parser": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", + "integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/marked": { + "version": "14.1.4", + "resolved": "https://registry.npmmirror.com/marked/-/marked-14.1.4.tgz", + "integrity": "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "license": "ISC", + "dependencies": { + "wildcard": "^1.1.0" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.27.0", + "resolved": "https://registry.npmmirror.com/preact/-/preact-10.27.0.tgz", + "integrity": "sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-visualizer": { + "version": "5.14.0", + "resolved": "https://registry.npmmirror.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz", + "integrity": "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^8.4.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "rolldown": "1.x", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sass": { + "version": "1.81.0", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-3.0.1.tgz", + "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slate": { + "version": "0.72.8", + "resolved": "https://registry.npmmirror.com/slate/-/slate-0.72.8.tgz", + "integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.6", + "is-plain-object": "^5.0.0", + "tiny-warning": "^1.0.3" + } + }, + "node_modules/slate-history": { + "version": "0.66.0", + "resolved": "https://registry.npmmirror.com/slate-history/-/slate-history-0.66.0.tgz", + "integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^5.0.0" + }, + "peerDependencies": { + "slate": ">=0.65.3" + } + }, + "node_modules/snabbdom": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.6.2.tgz", + "integrity": "sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==", + "license": "MIT", + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ssr-window": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz", + "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", + "license": "MIT" + }, + "node_modules/store": { + "version": "2.0.12", + "resolved": "https://registry.npmmirror.com/store/-/store-2.0.12.tgz", + "integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "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.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmmirror.com/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.39.1", + "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.39.1.tgz", + "integrity": "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.1", + "@typescript-eslint/parser": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmmirror.com/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "0.8.9", + "resolved": "https://registry.npmmirror.com/vite-plugin-inspect/-/vite-plugin-inspect-0.8.9.tgz", + "integrity": "sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.10", + "@rollup/pluginutils": "^5.1.3", + "debug": "^4.3.7", + "error-stack-parser-es": "^0.1.5", + "fs-extra": "^11.2.0", + "open": "^10.1.0", + "perfect-debounce": "^1.0.0", + "picocolors": "^1.1.1", + "sirv": "^3.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/vite-plugin-inspect/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.7.7.tgz", + "integrity": "sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^7.7.7", + "@vue/devtools-kit": "^7.7.7", + "@vue/devtools-shared": "^7.7.7", + "execa": "^9.5.2", + "sirv": "^3.0.1", + "vite-plugin-inspect": "0.8.9", + "vite-plugin-vue-inspector": "^5.3.1" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.18", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-echarts": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-7.0.3.tgz", + "integrity": "sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==", + "license": "MIT", + "dependencies": { + "vue-demi": "^0.13.11" + }, + "peerDependencies": { + "@vue/runtime-core": "^3.0.0", + "echarts": "^5.5.1", + "vue": "^2.7.0 || ^3.1.1" + }, + "peerDependenciesMeta": { + "@vue/runtime-core": { + "optional": true + } + } + }, + "node_modules/vue-echarts/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-i18n": { + "version": "11.1.11", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.11.tgz", + "integrity": "sha512-LvyteQoXeQiuILbzqv13LbyBna/TEv2Ha+4ZWK2AwGHUzZ8+IBaZS0TJkCgn5izSPLcgZwXy9yyTrewCb2u/MA==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "11.1.11", + "@intlify/shared": "11.1.11", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "license": "MIT", + "dependencies": { + "sortablejs": "1.14.0" + }, + "peerDependencies": { + "vue": "^3.0.1" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==", + "license": "MIT" + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yaml-eslint-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz", + "integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.0.0", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/cool-admin-vue/package.json b/cool-admin-vue/package.json new file mode 100644 index 0000000..d3d95ed --- /dev/null +++ b/cool-admin-vue/package.json @@ -0,0 +1,75 @@ +{ + "name": "cool-admin-vue", + "version": "8.0.0", + "type": "module", + "scripts": { + "dev": "vite --host", + "build": "vite build", + "build-static": "vite build --mode static", + "build-demo": "vite build --mode demo", + "preview": "vite preview", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --fix", + "format": "prettier --write src/" + }, + "dependencies": { + "@cool-vue/crud": "^8.0.6", + "@element-plus/icons-vue": "^2.3.1", + "@vueuse/core": "^12.5.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "axios": "^1.7.9", + "chardet": "^2.0.0", + "core-js": "^3.40.0", + "dayjs": "^1.11.13", + "echarts": "^5.6.0", + "element-plus": "2.10.2", + "file-saver": "^2.0.5", + "lodash-es": "^4.17.21", + "marked": "^14.1.3", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", + "pinia": "^2.3.1", + "store": "^2.0.12", + "vue": "^3.5.13", + "vue-echarts": "^7.0.3", + "vue-i18n": "^11.0.1", + "vue-router": "^4.5.0", + "vuedraggable": "^4.1.0", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@cool-vue/vite-plugin": "^8.2.2", + "@intlify/unplugin-vue-i18n": "^6.0.3", + "@rushstack/eslint-patch": "^1.10.5", + "@tsconfig/node20": "^20.1.4", + "@types/file-saver": "^2.0.7", + "@types/lodash-es": "^4.17.12", + "@types/mockjs": "^1.0.10", + "@types/node": "^20.17.17", + "@types/nprogress": "^0.2.3", + "@types/store": "^2.0.5", + "@vitejs/plugin-vue": "^5.2.1", + "@vitejs/plugin-vue-jsx": "^4.1.1", + "@vue/compiler-sfc": "^3.5.13", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.3.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.5.1", + "autoprefixer": "^10.4.20", + "eslint": "^9.19.0", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-vue": "^9.32.0", + "postcss": "^8.5.1", + "prettier": "^3.4.2", + "rollup-plugin-visualizer": "^5.14.0", + "sass": "1.81.0", + "tailwindcss": "^3.4.17", + "terser": "^5.36.0", + "typescript": "~5.5.4", + "vite": "^5.4.14", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-vue-devtools": "^7.7.1", + "vue-tsc": "^2.2.0" + } +} diff --git a/cool-admin-vue/pnpm-lock.yaml b/cool-admin-vue/pnpm-lock.yaml new file mode 100644 index 0000000..6032cd0 --- /dev/null +++ b/cool-admin-vue/pnpm-lock.yaml @@ -0,0 +1,5655 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@cool-vue/crud': + specifier: ^8.0.6 + version: 8.0.6(typescript@5.5.4) + '@element-plus/icons-vue': + specifier: ^2.3.1 + version: 2.3.1(vue@3.5.18(typescript@5.5.4)) + '@vueuse/core': + specifier: ^12.5.0 + version: 12.8.2(typescript@5.5.4) + '@wangeditor/editor': + specifier: ^5.1.23 + version: 5.1.23 + '@wangeditor/editor-for-vue': + specifier: ^5.1.12 + version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.18(typescript@5.5.4)) + axios: + specifier: ^1.7.9 + version: 1.11.0 + chardet: + specifier: ^2.0.0 + version: 2.1.0 + core-js: + specifier: ^3.40.0 + version: 3.44.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + echarts: + specifier: ^5.6.0 + version: 5.6.0 + element-plus: + specifier: 2.10.2 + version: 2.10.2(vue@3.5.18(typescript@5.5.4)) + file-saver: + specifier: ^2.0.5 + version: 2.0.5 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + marked: + specifier: ^14.1.3 + version: 14.1.4 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + pinia: + specifier: ^2.3.1 + version: 2.3.1(typescript@5.5.4)(vue@3.5.18(typescript@5.5.4)) + store: + specifier: ^2.0.12 + version: 2.0.12 + vue: + specifier: ^3.5.13 + version: 3.5.18(typescript@5.5.4) + vue-echarts: + specifier: ^7.0.3 + version: 7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.5.4)) + vue-i18n: + specifier: ^11.0.1 + version: 11.1.10(vue@3.5.18(typescript@5.5.4)) + vue-router: + specifier: ^4.5.0 + version: 4.5.1(vue@3.5.18(typescript@5.5.4)) + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0(vue@3.5.18(typescript@5.5.4)) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 + devDependencies: + '@cool-vue/vite-plugin': + specifier: ^8.2.2 + version: 8.2.2 + '@intlify/unplugin-vue-i18n': + specifier: ^6.0.3 + version: 6.0.8(@vue/compiler-dom@3.5.18)(eslint@9.31.0(jiti@1.21.7))(rollup@4.45.1)(typescript@5.5.4)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4)) + '@rushstack/eslint-patch': + specifier: ^1.10.5 + version: 1.12.0 + '@tsconfig/node20': + specifier: ^20.1.4 + version: 20.1.6 + '@types/file-saver': + specifier: ^2.0.7 + version: 2.0.7 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/mockjs': + specifier: ^1.0.10 + version: 1.0.10 + '@types/node': + specifier: ^20.17.17 + version: 20.19.9 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 + '@types/store': + specifier: ^2.0.5 + version: 2.0.5 + '@vitejs/plugin-vue': + specifier: ^5.2.1 + version: 5.2.4(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)) + '@vitejs/plugin-vue-jsx': + specifier: ^4.1.1 + version: 4.2.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)) + '@vue/compiler-sfc': + specifier: ^3.5.13 + version: 3.5.18 + '@vue/eslint-config-prettier': + specifier: ^10.2.0 + version: 10.2.0(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2) + '@vue/eslint-config-typescript': + specifier: ^14.3.0 + version: 14.6.0(eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + '@vue/tsconfig': + specifier: ^0.5.1 + version: 0.5.1 + autoprefixer: + specifier: ^10.4.20 + version: 10.4.21(postcss@8.5.6) + eslint: + specifier: ^9.19.0 + version: 9.31.0(jiti@1.21.7) + eslint-plugin-prettier: + specifier: ^5.2.3 + version: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2) + eslint-plugin-vue: + specifier: ^9.32.0 + version: 9.33.0(eslint@9.31.0(jiti@1.21.7)) + postcss: + specifier: ^8.5.1 + version: 8.5.6 + prettier: + specifier: ^3.4.2 + version: 3.6.2 + rollup-plugin-visualizer: + specifier: ^5.14.0 + version: 5.14.0(rollup@4.45.1) + sass: + specifier: 1.81.0 + version: 1.81.0 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + terser: + specifier: ^5.36.0 + version: 5.43.1 + typescript: + specifier: ~5.5.4 + version: 5.5.4 + vite: + specifier: ^5.4.14 + version: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + vite-plugin-compression: + specifier: ^0.5.1 + version: 0.5.1(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)) + vite-plugin-vue-devtools: + specifier: ^7.7.1 + version: 7.7.7(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)) + vue-tsc: + specifier: ^2.2.0 + version: 2.2.12(typescript@5.5.4) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + 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.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + engines: {node: '>=6.9.0'} + + '@cool-vue/crud@8.0.6': + resolution: {integrity: sha512-be6nKaUhdRmr2OQD6kJS6C+d1QTf6ibkp5fRSqs3x+lDuZOnyCh4/f/xg6ShKh2vtf+Bidwc2ftuxyEWx3A8SA==} + + '@cool-vue/vite-plugin@8.2.2': + resolution: {integrity: sha512-Fk3lKXXZJdP72Fnd9ndv95uN6kTQurvEHikU9AjD4Y6fLAgY4z+ljfGd3yRmMZWdN+bqD6OHVIiOm4ZOsAMAyQ==} + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@element-plus/icons-vue@2.3.1': + resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==} + peerDependencies: + vue: ^3.2.0 + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + 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.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.31.0': + resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.4': + resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.2': + resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} + + '@floating-ui/dom@1.7.2': + resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@intlify/bundle-utils@10.0.1': + resolution: {integrity: sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.1.10': + resolution: {integrity: sha512-JhRb40hD93Vk0BgMgDc/xMIFtdXPHoytzeK6VafBNOj6bb6oUZrGamXkBKecMsmGvDQQaPRGG2zpa25VCw8pyw==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@11.1.10': + resolution: {integrity: sha512-TABl3c8tSLWbcD+jkQTyBhrnW251dzqW39MPgEUCsd69Ua3ceoimsbIzvkcPzzZvt1QDxNkenMht+5//V3JvLQ==} + engines: {node: '>= 16'} + + '@intlify/shared@11.1.10': + resolution: {integrity: sha512-6ZW/f3Zzjxfa1Wh0tYQI5pLKUtU+SY7l70pEG+0yd0zjcsYcK0EBt6Fz30Dy0tZhEqemziQQy2aNU3GJzyrMUA==} + engines: {node: '>= 16'} + + '@intlify/unplugin-vue-i18n@6.0.8': + resolution: {integrity: sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue: ^3.2.25 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@vue/compiler-dom': ^3.0.0 + vue: ^3.0.0 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + '@intlify/shared': + optional: true + '@vue/compiler-dom': + optional: true + vue: + optional: true + vue-i18n: + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.10': + resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@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'} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@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'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rolldown/pluginutils@1.0.0-beta.29': + resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.45.1': + resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.45.1': + resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.45.1': + resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.1': + resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.45.1': + resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.1': + resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.45.1': + resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.45.1': + resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.45.1': + resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.45.1': + resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.45.1': + resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.45.1': + resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.45.1': + resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.45.1': + resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.45.1': + resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.1': + resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.1': + resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} + cpu: [x64] + os: [win32] + + '@rushstack/eslint-patch@1.12.0': + resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@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/node20@20.1.6': + resolution: {integrity: sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + + '@types/file-saver@2.0.7': + resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} + + '@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/mockjs@1.0.10': + resolution: {integrity: sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==} + + '@types/node@20.19.9': + resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/store@2.0.5': + resolution: {integrity: sha512-5NmTKe3GWdOaykzq7no+Ahf6mafJu0oLc9JNhJ3E26+0oFvd6GnksnZQpMXcH526mfG4xDYjFiKzyDL51PzeWQ==} + + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@typescript-eslint/eslint-plugin@8.38.0': + resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.38.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.38.0': + resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.38.0': + resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.38.0': + resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.38.0': + resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.38.0': + resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.38.0': + resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.38.0': + resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.38.0': + resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.38.0': + resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@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-jsx@4.2.0': + resolution: {integrity: sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} + + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + + '@vue/babel-helper-vue-transform-on@1.4.0': + resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==} + + '@vue/babel-plugin-jsx@1.4.0': + resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.4.0': + resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.18': + resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==} + + '@vue/compiler-dom@3.5.18': + resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==} + + '@vue/compiler-sfc@3.5.18': + resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==} + + '@vue/compiler-ssr@3.5.18': + resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/devtools-core@7.7.7': + resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==} + peerDependencies: + vue: ^3.0.0 + + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} + + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} + + '@vue/eslint-config-prettier@10.2.0': + resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==} + peerDependencies: + eslint: '>= 8.21.0' + prettier: '>= 3.0.0' + + '@vue/eslint-config-typescript@14.6.0': + resolution: {integrity: sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + eslint-plugin-vue: ^9.28.0 || ^10.0.0 + typescript: '>=4.8.4' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.18': + resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==} + + '@vue/runtime-core@3.5.18': + resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==} + + '@vue/runtime-dom@3.5.18': + resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==} + + '@vue/server-renderer@3.5.18': + resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==} + peerDependencies: + vue: 3.5.18 + + '@vue/shared@3.5.18': + resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} + + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + + '@vue/tsconfig@0.5.1': + resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + + '@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 + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + 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@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + birpc@2.5.0: + resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} + + 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@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + 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'} + + caniuse-lite@1.0.30001727: + resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} + + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + + 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'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + + 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@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + 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'} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + core-js@3.44.0: + resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.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@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.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'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + 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==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom7@3.0.0: + resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + 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==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + + electron-to-chromium@1.5.190: + resolution: {integrity: sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==} + + element-plus@2.10.2: + resolution: {integrity: sha512-p2KiAa0jEGXrzdlTAfpiS7HQFAhla4gvx6H7RuDf+OO0uC3DGpolxvdHjFR8gt7+vaWyxQNcHa1sAdBkmjqlgA==} + peerDependencies: + vue: ^3.2.0 + + element-plus@2.10.4: + resolution: {integrity: sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==} + 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==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-stack-parser-es@0.1.5: + resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==} + + 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'} + + 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.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.5.3: + resolution: {integrity: sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@9.33.0: + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.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-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + 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-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.31.0: + resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + 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@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'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + 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==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + 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'} + + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + engines: {node: '>=14.14'} + + 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==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + 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@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + 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 + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + 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==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + 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'} + + 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 + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + 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'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + + 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'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + 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-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + 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-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + 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 + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + 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@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + 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==} + + 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@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + marked@14.1.4: + resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==} + engines: {node: '>= 18'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + 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'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + 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 + + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + engines: {node: ^18 || >=20} + hasBin: true + + 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.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + 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@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + 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-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + 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-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + 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-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + 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'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + 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'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + 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.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + 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@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.26.9: + resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==} + + 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@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + 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'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + 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'} + + 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@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup-plugin-visualizer@5.14.0: + resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.45.1: + resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sass@1.81.0: + resolution: {integrity: sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==} + engines: {node: '>=14.0.0'} + hasBin: true + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + 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'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + + 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.2: + resolution: {integrity: sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==} + engines: {node: '>=12.17.0'} + + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + + ssr-window@3.0.0: + resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==} + + store@2.0.12: + resolution: {integrity: sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==} + + 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'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + 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 + + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + + 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'} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} + engines: {node: '>=10'} + hasBin: true + + 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==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typescript-eslint@8.38.0: + resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} + engines: {node: '>=14.0.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-hot-client@2.1.0: + resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} + peerDependencies: + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite-plugin-compression@0.5.1: + resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-inspect@0.8.9: + resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + vite-plugin-vue-devtools@7.7.7: + resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==} + engines: {node: '>=v14.21.3'} + peerDependencies: + vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite-plugin-vue-inspector@5.3.2: + resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} + peerDependencies: + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + + vue-demi@0.13.11: + resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + 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-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-echarts@7.0.3: + resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==} + peerDependencies: + '@vue/runtime-core': ^3.0.0 + echarts: ^5.5.1 + vue: ^2.7.0 || ^3.1.1 + peerDependenciesMeta: + '@vue/runtime-core': + optional: true + + vue-eslint-parser@10.2.0: + resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-i18n@11.1.10: + resolution: {integrity: sha512-C+IwnSg8QDSOAox0gdFYP5tsKLx5jNWxiawNoiNB/Tw4CReXmM1VJMXbduhbrEzAFLhreqzfDocuSVjGbxQrag==} + engines: {node: '>= 16'} + peerDependencies: + vue: ^3.0.0 + + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.18: + resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable@4.1.0: + resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} + peerDependencies: + vue: ^3.0.1 + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + 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==} + + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + + 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'} + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-eslint-parser@1.3.0: + resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + 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'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@antfu/utils@0.7.10': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.1 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.1 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.1 + + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.27.6': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@cool-vue/crud@8.0.6(typescript@5.5.4)': + dependencies: + '@vue/runtime-core': 3.5.18 + element-plus: 2.10.4(vue@3.5.18(typescript@5.5.4)) + lodash-es: 4.17.21 + vue: 3.5.18(typescript@5.5.4) + transitivePeerDependencies: + - '@vue/composition-api' + - typescript + + '@cool-vue/vite-plugin@8.2.2': + dependencies: + '@vue/compiler-sfc': 3.5.18 + axios: 1.11.0 + glob: 10.4.5 + lodash: 4.17.21 + magic-string: 0.30.17 + prettier: 3.6.2 + svgo: 3.3.2 + transitivePeerDependencies: + - debug + + '@ctrl/tinycolor@3.6.1': {} + + '@element-plus/icons-vue@2.3.1(vue@3.5.18(typescript@5.5.4))': + dependencies: + vue: 3.5.18(typescript@5.5.4) + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@1.21.7))': + dependencies: + eslint: 9.31.0(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.0': {} + + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.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@9.31.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.4': + dependencies: + '@eslint/core': 0.15.1 + levn: 0.4.1 + + '@floating-ui/core@1.7.2': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.2': + dependencies: + '@floating-ui/core': 1.7.2 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@intlify/bundle-utils@10.0.1(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))': + dependencies: + '@intlify/message-compiler': 11.1.10 + '@intlify/shared': 11.1.10 + acorn: 8.15.0 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.0 + mlly: 1.7.4 + source-map-js: 1.2.1 + yaml-eslint-parser: 1.3.0 + optionalDependencies: + vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4)) + + '@intlify/core-base@11.1.10': + dependencies: + '@intlify/message-compiler': 11.1.10 + '@intlify/shared': 11.1.10 + + '@intlify/message-compiler@11.1.10': + dependencies: + '@intlify/shared': 11.1.10 + source-map-js: 1.2.1 + + '@intlify/shared@11.1.10': {} + + '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.18)(eslint@9.31.0(jiti@1.21.7))(rollup@4.45.1)(typescript@5.5.4)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7)) + '@intlify/bundle-utils': 10.0.1(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4))) + '@intlify/shared': 11.1.10 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.18)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4)) + '@rollup/pluginutils': 5.2.0(rollup@4.45.1) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4) + debug: 4.4.1 + fast-glob: 3.3.3 + js-yaml: 4.1.0 + json5: 2.2.3 + pathe: 1.1.2 + picocolors: 1.1.1 + source-map-js: 1.2.1 + unplugin: 1.16.1 + vue: 3.5.18(typescript@5.5.4) + optionalDependencies: + vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.18)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@babel/parser': 7.28.0 + optionalDependencies: + '@intlify/shared': 11.1.10 + '@vue/compiler-dom': 3.5.18 + vue: 3.5.18(typescript@5.5.4) + vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4)) + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + 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.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.10': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@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 + + '@one-ini/wasm@0.1.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 + + '@pkgr/core@0.2.9': {} + + '@polka/url@1.0.0-next.29': {} + + '@rolldown/pluginutils@1.0.0-beta.29': {} + + '@rollup/pluginutils@5.2.0(rollup@4.45.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.45.1 + + '@rollup/rollup-android-arm-eabi@4.45.1': + optional: true + + '@rollup/rollup-android-arm64@4.45.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.1': + optional: true + + '@rollup/rollup-darwin-x64@4.45.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.1': + optional: true + + '@rushstack/eslint-patch@1.12.0': {} + + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@sxzz/popperjs-es@2.11.7': {} + + '@transloadit/prettier-bytes@0.0.7': {} + + '@trysound/sax@0.2.0': {} + + '@tsconfig/node20@20.1.6': {} + + '@types/estree@1.0.8': {} + + '@types/event-emitter@0.3.5': {} + + '@types/file-saver@2.0.7': {} + + '@types/json-schema@7.0.15': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + + '@types/mockjs@1.0.10': {} + + '@types/node@20.19.9': + dependencies: + undici-types: 6.21.0 + + '@types/nprogress@0.2.3': {} + + '@types/store@2.0.5': {} + + '@types/web-bluetooth@0.0.16': {} + + '@types/web-bluetooth@0.0.21': {} + + '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.38.0 + eslint: 9.31.0(jiti@1.21.7) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.38.0 + debug: 4.4.1 + eslint: 9.31.0(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.38.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.5.4) + '@typescript-eslint/types': 8.38.0 + debug: 4.4.1 + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.38.0': + dependencies: + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 + + '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + + '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + debug: 4.4.1 + eslint: 9.31.0(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.38.0': {} + + '@typescript-eslint/typescript-estree@8.38.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/project-service': 8.38.0(typescript@5.5.4) + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.5.4) + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4) + eslint: 9.31.0(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.38.0': + dependencies: + '@typescript-eslint/types': 8.38.0 + eslint-visitor-keys: 4.2.1 + + '@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.26.9 + + '@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-jsx@4.2.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0) + '@rolldown/pluginutils': 1.0.0-beta.29 + '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0) + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + vue: 3.5.18(typescript@5.5.4) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.2.4(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))': + dependencies: + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + vue: 3.5.18(typescript@5.5.4) + + '@volar/language-core@2.4.15': + dependencies: + '@volar/source-map': 2.4.15 + + '@volar/source-map@2.4.15': {} + + '@volar/typescript@2.4.15': + dependencies: + '@volar/language-core': 2.4.15 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/babel-helper-vue-transform-on@1.4.0': {} + + '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + '@vue/babel-helper-vue-transform-on': 1.4.0 + '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0) + '@vue/shared': 3.5.18 + optionalDependencies: + '@babel/core': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.0 + '@vue/compiler-sfc': 3.5.18 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.18': + dependencies: + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.18 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.18': + dependencies: + '@vue/compiler-core': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/compiler-sfc@3.5.18': + dependencies: + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.18 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.18': + dependencies: + '@vue/compiler-dom': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/devtools-core@7.7.7(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@vue/devtools-kit': 7.7.7 + '@vue/devtools-shared': 7.7.7 + mitt: 3.0.1 + nanoid: 5.1.5 + pathe: 2.0.3 + vite-hot-client: 2.1.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)) + vue: 3.5.18(typescript@5.5.4) + transitivePeerDependencies: + - vite + + '@vue/devtools-kit@7.7.7': + dependencies: + '@vue/devtools-shared': 7.7.7 + birpc: 2.5.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.7': + dependencies: + rfdc: 1.4.1 + + '@vue/eslint-config-prettier@10.2.0(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2)': + dependencies: + eslint: 9.31.0(jiti@1.21.7) + eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@1.21.7)) + eslint-plugin-prettier: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2) + prettier: 3.6.2 + transitivePeerDependencies: + - '@types/eslint' + + '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + eslint: 9.31.0(jiti@1.21.7) + eslint-plugin-vue: 9.33.0(eslint@9.31.0(jiti@1.21.7)) + fast-glob: 3.3.3 + typescript-eslint: 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + vue-eslint-parser: 10.2.0(eslint@9.31.0(jiti@1.21.7)) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@vue/language-core@2.2.12(typescript@5.5.4)': + dependencies: + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.18 + alien-signals: 1.0.13 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.5.4 + + '@vue/reactivity@3.5.18': + dependencies: + '@vue/shared': 3.5.18 + + '@vue/runtime-core@3.5.18': + dependencies: + '@vue/reactivity': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/runtime-dom@3.5.18': + dependencies: + '@vue/reactivity': 3.5.18 + '@vue/runtime-core': 3.5.18 + '@vue/shared': 3.5.18 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + vue: 3.5.18(typescript@5.5.4) + + '@vue/shared@3.5.18': {} + + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + + '@vue/tsconfig@0.5.1': {} + + '@vueuse/core@12.8.2(typescript@5.5.4)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2(typescript@5.5.4) + vue: 3.5.18(typescript@5.5.4) + transitivePeerDependencies: + - typescript + + '@vueuse/core@9.13.0(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.18(typescript@5.5.4)) + vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/shared@12.8.2(typescript@5.5.4)': + dependencies: + vue: 3.5.18(typescript@5.5.4) + transitivePeerDependencies: + - typescript + + '@vueuse/shared@9.13.0(vue@3.5.18(typescript@5.5.4))': + dependencies: + vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4)) + 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.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)': + 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.2) + 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.2 + + '@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.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + 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.2) + dom7: 3.0.0 + prismjs: 1.30.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@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.2)': + 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.2 + + '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.18(typescript@5.5.4))': + dependencies: + '@wangeditor/editor': 5.1.23 + vue: 3.5.18(typescript@5.5.4) + + '@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.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2) + '@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.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@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.2) + '@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.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@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.2))(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.2) + '@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.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@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.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2) + '@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.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2) + 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.2 + + '@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.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + 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.2) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@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.2))(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.2)': + 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.2) + 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.2 + + '@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.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@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.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)': + 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.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2) + '@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.2) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@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.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)': + 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.2) + dom7: 3.0.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.2 + + abbrev@2.0.0: {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + adler-32@1.3.1: {} + + 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 + + alien-signals@1.0.13: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.1 + caniuse-lite: 1.0.30001727 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + birpc@2.5.0: {} + + 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@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001727 + electron-to-chromium: 1.5.190 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + buffer-from@1.1.2: {} + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001727: {} + + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@2.1.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 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + codepage@1.15.0: {} + + 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@10.0.1: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + compute-scroll-into-view@1.0.20: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + convert-source-map@2.0.0: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + core-js@3.44.0: {} + + crc-32@1.2.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.1.3: {} + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dayjs@1.11.13: {} + + de-indent@1.0.2: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + default-browser-id@5.0.0: {} + + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.0 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + delayed-stream@1.0.0: {} + + detect-libc@1.0.3: + optional: true + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + 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 + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.2 + + electron-to-chromium@1.5.190: {} + + element-plus@2.10.2(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.1(vue@3.5.18(typescript@5.5.4)) + '@floating-ui/dom': 1.7.2 + '@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.18(typescript@5.5.4)) + async-validator: 4.2.5 + dayjs: 1.11.13 + escape-html: 1.0.3 + 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.18(typescript@5.5.4) + transitivePeerDependencies: + - '@vue/composition-api' + + element-plus@2.10.4(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.1(vue@3.5.18(typescript@5.5.4)) + '@floating-ui/dom': 1.7.2 + '@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.18(typescript@5.5.4)) + async-validator: 4.2.5 + dayjs: 1.11.13 + escape-html: 1.0.3 + 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.18(typescript@5.5.4) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + error-stack-parser-es@0.1.5: {} + + 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 + + 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.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)): + dependencies: + eslint: 9.31.0(jiti@1.21.7) + + eslint-plugin-prettier@5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2): + dependencies: + eslint: 9.31.0(jiti@1.21.7) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@1.21.7)) + + eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7)) + eslint: 9.31.0(jiti@1.21.7) + globals: 13.24.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.7.2 + vue-eslint-parser: 9.4.3(eslint@9.31.0(jiti@1.21.7)) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.31.0(jiti@1.21.7): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.15.1 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.31.0 + '@eslint/plugin-kit': 0.3.4 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.7 + 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@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + + ext@1.7.0: + dependencies: + type: 2.7.3 + + 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 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-saver@2.0.5: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.9: {} + + 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 + + frac@1.1.2: {} + + fraction.js@4.3.7: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + 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@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + 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 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globals@14.0.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hookable@5.5.3: {} + + html-void-elements@2.0.1: {} + + human-signals@8.0.1: {} + + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.27.6 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immer@9.0.21: {} + + immutable@5.1.3: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hotkey@0.2.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@5.0.0: {} + + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + + is-url@1.2.4: {} + + is-what@4.1.16: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.0: + dependencies: + acorn: 8.15.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.7.2 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kolorist@1.8.0: {} + + 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: {} + + 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@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + marked@14.1.4: {} + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + memoize-one@6.0.0: {} + + merge2@1.4.1: {} + + 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 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + mitt@3.0.1: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + 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: {} + + nanoid@5.1.5: {} + + natural-compare@1.4.0: {} + + next-tick@1.1.0: {} + + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.19: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + open@10.2.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.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 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-ms@4.0.0: {} + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pify@2.3.0: {} + + pinia@2.3.1(typescript@5.5.4)(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.18(typescript@5.5.4) + vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4)) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - '@vue/composition-api' + + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + 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.10 + + postcss-js@4.0.1(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-load-config@4.0.2(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.0 + optionalDependencies: + 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-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.26.9: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.6.2: {} + + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + + prismjs@1.30.0: {} + + proto-list@1.2.4: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rollup-plugin-visualizer@5.14.0(rollup@4.45.1): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.4 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.45.1 + + rollup@4.45.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.1 + '@rollup/rollup-android-arm64': 4.45.1 + '@rollup/rollup-darwin-arm64': 4.45.1 + '@rollup/rollup-darwin-x64': 4.45.1 + '@rollup/rollup-freebsd-arm64': 4.45.1 + '@rollup/rollup-freebsd-x64': 4.45.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 + '@rollup/rollup-linux-arm-musleabihf': 4.45.1 + '@rollup/rollup-linux-arm64-gnu': 4.45.1 + '@rollup/rollup-linux-arm64-musl': 4.45.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-musl': 4.45.1 + '@rollup/rollup-linux-s390x-gnu': 4.45.1 + '@rollup/rollup-linux-x64-gnu': 4.45.1 + '@rollup/rollup-linux-x64-musl': 4.45.1 + '@rollup/rollup-win32-arm64-msvc': 4.45.1 + '@rollup/rollup-win32-ia32-msvc': 4.45.1 + '@rollup/rollup-win32-x64-msvc': 4.45.1 + fsevents: 2.3.3 + + run-applescript@7.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sass@1.81.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + semver@6.3.1: {} + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + 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.2: {} + + sortablejs@1.14.0: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + speakingurl@14.0.1: {} + + ssf@0.11.2: + dependencies: + frac: 1.1.2 + + ssr-window@3.0.0: {} + + store@2.0.12: {} + + 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.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@4.0.0: {} + + strip-json-comments@3.1.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + 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 + + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + tailwindcss@3.4.17: + 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.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + terser@5.43.1: + dependencies: + '@jridgewell/source-map': 0.3.10 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tiny-warning@1.0.3: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + totalist@3.0.1: {} + + ts-api-utils@2.1.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + + ts-interface-checker@0.1.13: {} + + tslib@2.3.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type@2.7.3: {} + + typescript-eslint@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4): + dependencies: + '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4) + eslint: 9.31.0(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + typescript@5.5.4: {} + + ufo@1.6.1: {} + + undici-types@6.21.0: {} + + unicorn-magic@0.3.0: {} + + universalify@2.0.1: {} + + unplugin@1.16.1: + dependencies: + acorn: 8.15.0 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite-hot-client@2.1.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)): + dependencies: + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + + vite-plugin-compression@0.5.1(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)): + dependencies: + chalk: 4.1.2 + debug: 4.4.1 + fs-extra: 10.1.0 + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + transitivePeerDependencies: + - supports-color + + vite-plugin-inspect@0.8.9(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)): + dependencies: + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.2.0(rollup@4.45.1) + debug: 4.4.1 + error-stack-parser-es: 0.1.5 + fs-extra: 11.3.0 + open: 10.2.0 + perfect-debounce: 1.0.0 + picocolors: 1.1.1 + sirv: 3.0.1 + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + transitivePeerDependencies: + - rollup + - supports-color + + vite-plugin-vue-devtools@7.7.7(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@vue/devtools-core': 7.7.7(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)) + '@vue/devtools-kit': 7.7.7 + '@vue/devtools-shared': 7.7.7 + execa: 9.6.0 + sirv: 3.0.1 + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + vite-plugin-inspect: 0.8.9(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)) + vite-plugin-vue-inspector: 5.3.2(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)) + transitivePeerDependencies: + - '@nuxt/kit' + - rollup + - supports-color + - vue + + vite-plugin-vue-inspector@5.3.2(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)): + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0) + '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0) + '@vue/compiler-dom': 3.5.18 + kolorist: 1.8.0 + magic-string: 0.30.17 + vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1) + transitivePeerDependencies: + - supports-color + + vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.45.1 + optionalDependencies: + '@types/node': 20.19.9 + fsevents: 2.3.3 + sass: 1.81.0 + terser: 5.43.1 + + vscode-uri@3.1.0: {} + + vue-component-type-helpers@2.2.12: {} + + vue-demi@0.13.11(vue@3.5.18(typescript@5.5.4)): + dependencies: + vue: 3.5.18(typescript@5.5.4) + + vue-demi@0.14.10(vue@3.5.18(typescript@5.5.4)): + dependencies: + vue: 3.5.18(typescript@5.5.4) + + vue-echarts@7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.5.4)): + dependencies: + echarts: 5.6.0 + vue: 3.5.18(typescript@5.5.4) + vue-demi: 0.13.11(vue@3.5.18(typescript@5.5.4)) + optionalDependencies: + '@vue/runtime-core': 3.5.18 + transitivePeerDependencies: + - '@vue/composition-api' + + vue-eslint-parser@10.2.0(eslint@9.31.0(jiti@1.21.7)): + dependencies: + debug: 4.4.1 + eslint: 9.31.0(jiti@1.21.7) + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + vue-eslint-parser@9.4.3(eslint@9.31.0(jiti@1.21.7)): + dependencies: + debug: 4.4.1 + eslint: 9.31.0(jiti@1.21.7) + 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.2 + transitivePeerDependencies: + - supports-color + + vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@intlify/core-base': 11.1.10 + '@intlify/shared': 11.1.10 + '@vue/devtools-api': 6.6.4 + vue: 3.5.18(typescript@5.5.4) + + vue-router@4.5.1(vue@3.5.18(typescript@5.5.4)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.18(typescript@5.5.4) + + vue-tsc@2.2.12(typescript@5.5.4): + dependencies: + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.5.4) + typescript: 5.5.4 + + vue@3.5.18(typescript@5.5.4): + dependencies: + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-sfc': 3.5.18 + '@vue/runtime-dom': 3.5.18 + '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.5.4)) + '@vue/shared': 3.5.18 + optionalDependencies: + typescript: 5.5.4 + + vuedraggable@4.1.0(vue@3.5.18(typescript@5.5.4)): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.18(typescript@5.5.4) + + webpack-virtual-modules@0.6.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wildcard@1.1.2: {} + + wmf@1.0.2: {} + + word-wrap@1.2.5: {} + + word@0.3.0: {} + + 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.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + + xml-name-validator@4.0.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml-eslint-parser@1.3.0: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.8.0 + + yaml@2.8.0: {} + + 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 + + yocto-queue@0.1.0: {} + + yoctocolors@2.1.1: {} + + zrender@5.6.1: + dependencies: + tslib: 2.3.0 diff --git a/cool-admin-vue/pnpm-workspace.yaml b/cool-admin-vue/pnpm-workspace.yaml new file mode 100644 index 0000000..66e446c --- /dev/null +++ b/cool-admin-vue/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +onlyBuiltDependencies: + - '@parcel/watcher' + - core-js + - es5-ext + - esbuild + - vue-demi diff --git a/cool-admin-vue/postcss.config.js b/cool-admin-vue/postcss.config.js new file mode 100644 index 0000000..0f77216 --- /dev/null +++ b/cool-admin-vue/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; diff --git a/cool-admin-vue/src/App.vue b/cool-admin-vue/src/App.vue new file mode 100644 index 0000000..359a98b --- /dev/null +++ b/cool-admin-vue/src/App.vue @@ -0,0 +1,8 @@ + + + diff --git a/cool-admin-vue/src/config/dev.ts b/cool-admin-vue/src/config/dev.ts new file mode 100644 index 0000000..0501cd2 --- /dev/null +++ b/cool-admin-vue/src/config/dev.ts @@ -0,0 +1,9 @@ +import { host, value } from './proxy'; + +export default { + // 根地址 + host, + + // 请求地址 + baseUrl: `/${value}` +}; diff --git a/cool-admin-vue/src/config/index.ts b/cool-admin-vue/src/config/index.ts new file mode 100644 index 0000000..3844c5d --- /dev/null +++ b/cool-admin-vue/src/config/index.ts @@ -0,0 +1,62 @@ +import { storage } from '../cool'; +import dev from './dev'; +import prod from './prod'; + +// 是否开发模式 +export const isDev = import.meta.env.DEV; + +// 配置 +export const config = { + // 项目信息 + app: { + name: import.meta.env.VITE_NAME, + + // 菜单 + menu: { + // 是否分组显示 + isGroup: false, + // 自定义菜单列表 + list: [] + }, + + // 路由 + router: { + // 模式 + mode: import.meta.env.MODE == 'static' ? 'hash' : 'history', + // 转场动画 + transition: 'slide' + } + }, + + // 国际化配置 + i18n: { + locale: storage.get('locale') || 'zh-cn', + languages: [ + { + label: '中文', + value: 'zh-cn' + }, + { + label: '繁体中文', + value: 'zh-tw' + }, + { + label: 'English', + value: 'en' + } + ] + }, + + // 忽略规则 + ignore: { + // 不显示请求进度条 + NProgress: ['__cool_*'], + // 页面不需要登录验证 + token: [] + }, + + // 当前环境 + ...(isDev ? dev : prod) +}; + +export * from './proxy'; diff --git a/cool-admin-vue/src/config/prod.ts b/cool-admin-vue/src/config/prod.ts new file mode 100644 index 0000000..a86fbc5 --- /dev/null +++ b/cool-admin-vue/src/config/prod.ts @@ -0,0 +1,17 @@ +import { proxy } from './proxy'; + +export default { + // 根地址 + host: proxy['/prod/'].target, + + // 请求地址 + get baseUrl() { + const mode = import.meta.env.MODE; + + if (mode == 'static') { + return location.origin; + } else { + return '/api'; + } + } +}; diff --git a/cool-admin-vue/src/config/proxy.ts b/cool-admin-vue/src/config/proxy.ts new file mode 100644 index 0000000..4f4e5db --- /dev/null +++ b/cool-admin-vue/src/config/proxy.ts @@ -0,0 +1,18 @@ +const proxy = { + '/dev/': { + target: 'http://127.0.0.1:8001', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/dev/, '') + }, + + '/prod/': { + target: 'https://show.cool-admin.com', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/prod/, '/api') + } +}; + +const value = 'dev'; +const host = proxy[`/${value}/`]?.target; + +export { proxy, host, value }; diff --git a/cool-admin-vue/src/cool/bootstrap/eps.ts b/cool-admin-vue/src/cool/bootstrap/eps.ts new file mode 100644 index 0000000..f42ce08 --- /dev/null +++ b/cool-admin-vue/src/cool/bootstrap/eps.ts @@ -0,0 +1,62 @@ +import { merge } from 'lodash-es'; +import { BaseService, service } from '../service'; +import { isDev } from '/@/config'; +import { eps } from 'virtual:eps'; +import { hmr } from '../hooks'; + +export function createEps() { + // 设置 request 方法 + function set(d: any) { + if (d.namespace) { + const a = new BaseService(d.namespace); + + for (const i in d) { + const { path, method = 'get' } = d[i]; + + if (path) { + a.request = a.request; + + a[i] = function (data?: any) { + return this.request({ + url: path, + method, + [method.toLocaleLowerCase() == 'post' ? 'data' : 'params']: data + }); + }; + } + } + + for (const i in a) { + d[i] = a[i]; + } + } else { + for (const i in d) { + set(d[i]); + } + } + } + + // 遍历每一个方法 + set(eps.service); + + // 合并 eps + merge(service, eps.service); + + // 热更新处理 + hmr.setData('service', service); + + // 提示 + if (isDev) { + console.log('[cool-eps] updated'); + } +} + +// 监听 vite 触发事件 +if (import.meta.hot) { + import.meta.hot.on('eps-update', ({ service }) => { + if (service) { + eps.service = service; + } + createEps(); + }); +} diff --git a/cool-admin-vue/src/cool/bootstrap/index.ts b/cool-admin-vue/src/cool/bootstrap/index.ts new file mode 100644 index 0000000..fa4bd06 --- /dev/null +++ b/cool-admin-vue/src/cool/bootstrap/index.ts @@ -0,0 +1,24 @@ +import { createPinia } from 'pinia'; +import { type App } from 'vue'; +import { createModule } from './module'; +import { router } from '../router'; +import { Loading } from '../utils'; +import { createEps } from './eps'; +import 'virtual:svg-register'; + +export async function bootstrap(app: App) { + // pinia + app.use(createPinia()); + + // 路由 + app.use(router); + + // 模块 + const { eventLoop } = createModule(app); + + // eps + createEps(); + + // 加载 + Loading.set([eventLoop()]); +} diff --git a/cool-admin-vue/src/cool/bootstrap/module.ts b/cool-admin-vue/src/cool/bootstrap/module.ts new file mode 100644 index 0000000..7c3c462 --- /dev/null +++ b/cool-admin-vue/src/cool/bootstrap/module.ts @@ -0,0 +1,126 @@ +import { type App, type Directive } from 'vue'; +import { assign, isFunction, orderBy, mergeWith } from 'lodash-es'; +import { filename } from '../utils'; +import { module } from '../module'; +import { hmr } from '../hooks'; +import { config } from '/@/config'; + +// 扫描文件 +const files = import.meta.glob('/src/{modules,plugins}/*/{config.ts,service/**,directives/**}', { + eager: true, + import: 'default' +}); + +// 模块列表 +module.list = hmr.getData('modules', []); + +// 解析 +for (const i in files) { + // 分割 + const [, , type, name, action] = i.split('/'); + + // 文件名 + const n = filename(i); + + // 文件内容 + const v = files[i]; + + // 模块是否存在 + const m = module.get(name); + + // 数据 + const d = m || { + name, + type, + value: null, + services: [], + directives: [] + }; + + // 配置 + if (action == 'config.ts') { + d.value = v; + } + // 服务 + else if (action == 'service') { + const s = new (v as any)(); + + if (s) { + d.services?.push({ + path: s.namespace, + value: s + }); + } + } + // 指令 + else if (action == 'directives') { + d.directives?.push({ name: n, value: v as Directive }); + } + + if (!m) { + module.add(d); + } +} + +// 创建 +export function createModule(app: App) { + // 排序 + module.list.forEach(e => { + const d = isFunction(e.value) ? e.value(app) : e.value; + + if (d) { + assign(e, d); + } + + if (!d.order) { + e.order = 0; + } + }); + + const list = orderBy(module.list, 'order', 'desc').map(e => { + if (e.enable !== false) { + // 初始化 + e.install?.(app, e.options); + + // 注册组件 + e.components?.forEach(async (c: any) => { + const v = await (isFunction(c) ? c() : c); + const n = v.default || v; + + if (n.name) { + app.component(n.name, n); + } + }); + + // 注册指令 + e.directives?.forEach(v => { + app.directive(v.name, v.value); + }); + + // 合并忽略配置 + config.ignore = mergeWith({}, config.ignore, e.ignore, (a, b) => a?.concat(b)); + } + + // 附加值 + e.pages?.forEach(v => { + v.isPage = true; + }); + + return e; + }); + + return { + // 模块列表 + list, + // 事件加载 + async eventLoop() { + const events: any = {}; + + for (let i = 0; i < list.length; i++) { + if (list[i].onLoad) { + assign(events, await list[i]?.onLoad?.(events)); + } + } + } + }; +} diff --git a/cool-admin-vue/src/cool/hooks/browser.ts b/cool-admin-vue/src/cool/hooks/browser.ts new file mode 100644 index 0000000..77c2a9f --- /dev/null +++ b/cool-admin-vue/src/cool/hooks/browser.ts @@ -0,0 +1,41 @@ +import { useEventListener } from '@vueuse/core'; +import { reactive, watch } from 'vue'; +import { getBrowser } from '../utils'; + +// 使用 reactive 创建一个响应式的浏览器对象 +const browser = reactive(getBrowser()); + +// 存储屏幕变化事件的回调函数数组 +const events: (() => void)[] = []; + +// 监听浏览器屏幕属性的变化 +watch( + () => browser.screen, // 监听的属性 + () => { + // 当屏幕属性变化时,执行所有注册的回调函数 + events.forEach(ev => ev()); + } +); + +// 监听窗口的 resize 事件,并更新浏览器对象 +useEventListener(window, 'resize', () => { + // 使用 Object.assign 更新响应式对象的属性 + Object.assign(browser, getBrowser()); +}); + +// 导出一个自定义的 hook +export function useBrowser() { + return { + browser, // 返回响应式的浏览器对象 + // 注册屏幕变化的回调函数 + onScreenChange(ev: () => void, immediate = true) { + // 将回调函数添加到事件数组中 + events.push(ev); + + // 如果 immediate 为 true,立即执行回调函数 + if (immediate) { + ev(); + } + } + }; +} diff --git a/cool-admin-vue/src/cool/hooks/hmr.ts b/cool-admin-vue/src/cool/hooks/hmr.ts new file mode 100644 index 0000000..e9d4b15 --- /dev/null +++ b/cool-admin-vue/src/cool/hooks/hmr.ts @@ -0,0 +1,32 @@ +// 解决热更新后数据失效问题 +// 初始化数据对象,如果热更新数据存在则使用它 +const data = import.meta.hot?.data.getData?.() || {}; + +// 检查是否支持热更新 +if (import.meta.hot) { + // 将当前数据存储函数赋值给热更新数据对象 + import.meta.hot.data.getData = () => { + return data; + }; +} + +// 导出一个热更新模块对象 +export const hmr = { + data, // 当前数据对象 + + // 设置数据的方法 + setData(key: string, value: any) { + // 将指定键值对存入数据对象 + data[key] = value; + }, + + // 获取数据的方法 + getData(key: string, defaultValue?: any) { + // 如果指定键不存在且提供了默认值,则设置默认值 + if (defaultValue !== undefined && !data[key]) { + this.setData(key, defaultValue); + } + // 返回指定键的值 + return data[key]; + } +}; diff --git a/cool-admin-vue/src/cool/hooks/index.ts b/cool-admin-vue/src/cool/hooks/index.ts new file mode 100644 index 0000000..ff551a7 --- /dev/null +++ b/cool-admin-vue/src/cool/hooks/index.ts @@ -0,0 +1,58 @@ +import { getCurrentInstance, type Ref, reactive } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { service } from '../service'; +import { useBrowser } from './browser'; +import { useMitt } from './mitt'; + +// 创建一个响应式的 refs 对象,并提供设置 refs 的方法 +export function useRefs() { + const refs = reactive<{ [key: string]: any }>({}); + + // 设置 refs 的方法,返回一个函数用于更新特定 ref + function setRefs(name: string) { + return (el: any) => { + refs[name] = el; + return () => refs[name]; // 返回一个函数用于获取当前 ref + }; + } + + return { refs, setRefs }; +} + +// 获取指定名称的父组件实例,并将其暴露的属性赋值给传入的 Ref +export function useParent(name: string, r: Ref) { + const instance = getCurrentInstance(); + + if (instance) { + let parent = instance.proxy?.$.parent; + + // 遍历父组件链,直到找到匹配的组件名称 + while (parent && parent.type?.name !== name) { + parent = parent?.parent; + } + + // 如果找到匹配的父组件,将其暴露的属性赋值给 Ref + if (parent && parent.type.name === name) { + r.value = parent.exposed; + } + } + + return r; +} + +// 组合多个功能模块,返回一个包含服务、路由、事件总线等的对象 +export function useCool() { + return { + service, + route: useRoute(), + router: useRouter(), + mitt: useMitt(), + ...useBrowser(), + ...useRefs() + }; +} + +// 导出其他模块的功能 +export * from './browser'; +export * from './hmr'; +export * from './mitt'; diff --git a/cool-admin-vue/src/cool/hooks/mitt.ts b/cool-admin-vue/src/cool/hooks/mitt.ts new file mode 100644 index 0000000..a530ac0 --- /dev/null +++ b/cool-admin-vue/src/cool/hooks/mitt.ts @@ -0,0 +1,9 @@ +import Mitt, { type Emitter } from 'mitt'; +import { hmr } from './hmr'; + +export const mitt: Emitter = hmr.getData('mitt', Mitt()); + +// 返回 mitt 实例,用于在应用中进行事件的发布和订阅 +export function useMitt() { + return mitt; +} diff --git a/cool-admin-vue/src/cool/index.ts b/cool-admin-vue/src/cool/index.ts new file mode 100644 index 0000000..bb7cc6a --- /dev/null +++ b/cool-admin-vue/src/cool/index.ts @@ -0,0 +1,7 @@ +export * from './service'; +export * from './bootstrap'; +export * from './hooks'; +export * from './module'; +export * from './router'; +export * from './types'; +export { storage } from './utils'; diff --git a/cool-admin-vue/src/cool/index.vue b/cool-admin-vue/src/cool/index.vue new file mode 100644 index 0000000..7ee30f4 --- /dev/null +++ b/cool-admin-vue/src/cool/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/cool-admin-vue/src/cool/module/index.ts b/cool-admin-vue/src/cool/module/index.ts new file mode 100644 index 0000000..a289d67 --- /dev/null +++ b/cool-admin-vue/src/cool/module/index.ts @@ -0,0 +1,43 @@ +import type { Module } from '../types'; +import { hmr } from '../hooks'; +import { ctx } from 'virtual:ctx'; + +// 获取模块列表,若不存在则初始化为空数组 +const list: Module[] = hmr.getData('modules', []); + +// 定义模块对象 +const module = { + // 模块列表 + list, + + // 模块目录 + dirs: ctx.modules, + + // 请求对象,初始化为已解决的 Promise + req: Promise.resolve(), + + // 根据名称获取模块 + get(name: string): Module { + // 使用 find 方法查找模块,假设模块名称是唯一的 + return this.list.find(e => e.name == name)!; + }, + + // 获取模块的配置选项 + config(name: string) { + // 如果模块存在,返回其配置选项,否则返回空对象 + return this.get(name).options || {}; + }, + + // 添加新模块到列表中 + add(data: Module) { + this.list.push(data); + }, + + // 返回请求对象 + wait() { + return this.req; + } +}; + +// 导出模块对象 +export { module }; diff --git a/cool-admin-vue/src/cool/router/index.ts b/cool-admin-vue/src/cool/router/index.ts new file mode 100644 index 0000000..41b4165 --- /dev/null +++ b/cool-admin-vue/src/cool/router/index.ts @@ -0,0 +1,247 @@ +import { ElMessage } from 'element-plus'; +import { + createRouter, + createRouterMatcher, + createWebHashHistory, + createWebHistory, + type RouteRecordRaw +} from 'vue-router'; +import { type Router, storage, module } from '/@/cool'; +import { isArray } from 'lodash-es'; +import { useBase } from '/$/base'; +import { Loading } from '../utils'; +import { config, isDev } from '/@/config'; + +// 基本路径 +const baseUrl = import.meta.env.BASE_URL; + +// 扫描文件 +const files = import.meta.glob(['/src/modules/*/{views,pages}/**/*', '!**/components']); + +// 默认路由 +const routes: RouteRecordRaw[] = [ + { + path: '/', + name: 'index', + component: () => import('/$/base/pages/main/index.vue'), + children: [] + }, + { + path: '/:catchAll(.*)', + name: '404', + component: () => import('/$/base/pages/error/404.vue') + } +]; + +// 创建路由器 +const router = createRouter({ + history: + config.app.router.mode == 'history' + ? createWebHistory(baseUrl) + : createWebHashHistory(baseUrl), + routes +}) as Router; + +// 组件加载后 +router.beforeResolve(() => { + Loading.close(); +}); + +let lock = false; + +// 错误监听 +router.onError((error: Error) => { + if (!lock) { + lock = true; + + // 显示错误信息 + ElMessage.error(`页面存在错误:${error.message}`); + console.error(error); + + // 如果是动态加载模块失败的错误,且非开发环境,则刷新页面 + if (error.message?.includes('Failed to fetch dynamically imported module')) { + if (!isDev) { + window.location.reload(); + } + } + + // 短暂延迟后解锁,允许后续错误处理 + setTimeout(() => { + lock = false; + }, 0); + } +}); + +// 添加视图,页面路由 +router.append = function (routeData) { + if (!routeData) { + return false; // 如果没有路由数据,直接返回 + } + + // 确保 routeData 是数组 + const routeList = isArray(routeData) ? routeData : [routeData]; + + routeList.forEach(route => { + if (!route.meta) { + route.meta = {}; // 初始化 meta 对象 + } + + // 如果没有指定组件路径 + if (!route.component) { + const viewPath = route.viewPath; + + if (viewPath) { + if (viewPath.startsWith('http')) { + // 如果是外部链接,使用 iframe 组件 + route.meta.iframeUrl = viewPath; + route.component = () => import('/$/base/views/frame.vue'); + } else { + // 从文件系统中动态导入组件 + route.component = files['/src/' + viewPath.replace('cool/', '')]; + } + } else if (!route.redirect) { + // 如果没有组件路径且没有重定向,默认重定向到 404 + route.redirect = '/404'; + } + } + + // 支持 props 接收参数 + route.props = true; + + // 标记为动态添加的路由 + route.meta.dynamic = true; + + // 判断是页面还是视图,并添加到相应的路由 + if (route.isPage || route.viewPath?.includes('/pages/')) { + router.addRoute(route); + } else { + router.addRoute('index', route); + } + }); +}; + +// 删除路由 +router.del = function (routeName) { + const allRoutes = router.getRoutes(); + + allRoutes.forEach(route => { + if (route.name === routeName) { + router.removeRoute(routeName); // 移除指定名称的路由 + } + }); +}; + +// 清空路由 +router.clear = function () { + const allRoutes = router.getRoutes(); + + allRoutes.forEach(route => { + if (route.name && route.meta?.dynamic) { + router.removeRoute(route.name); // 移除所有动态添加的路由 + } + }); +}; + +// 找路由 +router.find = function (path: string) { + const { menu } = useBase(); + + // 获取已注册的路由 + const registeredRoutes = router.getRoutes(); + + // 构建路由列表,包括已注册的路由、菜单配置和模块自定义路由 + const routeList: any[] = [ + ...registeredRoutes.map(route => ({ + ...route, + isReg: true + })), + ...menu.routes, + ...module.list.flatMap(module => (module.views || []).concat(module.pages || [])) + ]; + + let isRegistered = false; + let matchedRoute: (typeof routeList)[number] | undefined; + + // 创建路由匹配器 + const matcher = createRouterMatcher(routeList, {}); + + // 查找匹配的路由 + matcher.getRoutes().find(route => { + const routeRegex = new RegExp(route.re); + + if (routeRegex.test(path)) { + if (path === '/') { + // 如果路径是根路径,查找标记为首页的路由 + matchedRoute = routeList.find(route => route.meta?.isHome); + } else { + // 否则查找路径匹配且名称不是 'index' 的路由 + matchedRoute = routeList.find( + r => r.path === route.record.path && r.name !== 'index' + ); + } + + if (matchedRoute) { + isRegistered = !!matchedRoute.isReg; // 检查路由是否已注册 + } + + return true; + } + return false; + }); + + return { + route: matchedRoute, + isReg: isRegistered + }; +}; + +// 路由守卫 +router.beforeEach(async (to, from, next) => { + // 等待应用配置加载完 + await Loading.wait(); + + // 获取用户和进程数据 + const { user, process } = useBase(); + + // 查找路由信息 + const { isReg, route } = router.find(to.path); + + // 如果路由不存在 + if (!route) { + next(user.token ? '/404' : '/login'); // 根据用户登录状态重定向 + return; + } + + // 如果路由未注册 + if (!isReg) { + router.append(route); // 注册路由 + next(to.fullPath); // 重定向到原路径 + return; + } + + // 如果用户已登录 + if (user.token) { + if (to.path.includes('/login')) { + // 如果在登录页且 Token 未过期,重定向到首页 + if (!storage.isExpired('token')) { + next('/'); + return; + } + } else { + process.add(to); // 添加路由进程 + } + } else { + // 清除用户信息 + user.clear(); + + // 如果路径不在忽略 Token 验证的列表中,重定向到登录页 + if (!config.ignore.token.some(ignorePath => to.path === ignorePath)) { + next('/login'); + return; + } + } + + next(); // 继续导航 +}); + +export { router }; diff --git a/cool-admin-vue/src/cool/service/base.ts b/cool-admin-vue/src/cool/service/base.ts new file mode 100644 index 0000000..5cee840 --- /dev/null +++ b/cool-admin-vue/src/cool/service/base.ts @@ -0,0 +1,86 @@ +import { config } from '/@/config'; +import { request } from './request'; +import { AxiosRequestConfig } from 'axios'; + +export class BaseService { + namespace?: string; + + constructor(namespace?: string) { + if (namespace) { + this.namespace = namespace; + } + } + + // 发送请求 + async request(options: AxiosRequestConfig = {}) { + let url = options.url; + + if (url && url.indexOf('http') < 0) { + if (this.namespace) { + url = this.namespace + url; + } + + if (options.proxy !== false) { + url = config.baseUrl + '/' + url; + } + } + + return request({ + ...options, + url + }); + } + + // 获取列表 + async list(data: any) { + return this.request({ + url: '/list', + method: 'POST', + data + }); + } + + // 分页查询 + async page(data: any) { + return this.request({ + url: '/page', + method: 'POST', + data + }); + } + + // 获取信息 + async info(params: any) { + return this.request({ + url: '/info', + params + }); + } + + // 更新数据 + async update(data: any) { + return this.request({ + url: '/update', + method: 'POST', + data + }); + } + + // 删除数据 + async delete(data: any) { + return this.request({ + url: '/delete', + method: 'POST', + data + }); + } + + // 添加数据 + async add(data: any) { + return this.request({ + url: '/add', + method: 'POST', + data + }); + } +} diff --git a/cool-admin-vue/src/cool/service/index.ts b/cool-admin-vue/src/cool/service/index.ts new file mode 100644 index 0000000..1474751 --- /dev/null +++ b/cool-admin-vue/src/cool/service/index.ts @@ -0,0 +1,10 @@ +import { hmr } from '../hooks'; +import { BaseService } from './base'; + +// service 数据集合 +export const service: Eps.Service = hmr.getData('service', { + request: new BaseService().request +}); + +export * from './base'; +export * from './stream'; diff --git a/cool-admin-vue/src/cool/service/request.ts b/cool-admin-vue/src/cool/service/request.ts new file mode 100644 index 0000000..51b16d5 --- /dev/null +++ b/cool-admin-vue/src/cool/service/request.ts @@ -0,0 +1,168 @@ +import axios from 'axios'; +import NProgress from 'nprogress'; +import 'nprogress/nprogress.css'; +import { ElMessage } from 'element-plus'; +import { endsWith } from 'lodash-es'; +import { storage } from '/@/cool/utils'; +import { useBase } from '/$/base'; +import { router } from '../router'; +import { config, isDev } from '/@/config'; + +// 创建 axios 实例 +const request = axios.create({ + timeout: import.meta.env.VITE_TIMEOUT, // 设置请求超时时间 + withCredentials: false // 不携带凭证 +}); + +// 配置 NProgress +NProgress.configure({ + showSpinner: true // 显示加载指示器 +}); + +// 请求队列,用于存储待处理的请求 +let queue: Array<(token: string) => void> = []; + +// 标识是否正在刷新 token +let isRefreshing = false; + +// 请求拦截器 +request.interceptors.request.use( + (req: any) => { + const { user } = useBase(); // 获取用户信息 + + if (req.url) { + // 控制请求进度条的显示 + if ( + !config.ignore.NProgress.some(e => req.url.match(new RegExp(`${e}.*`))) && + (req.NProgress ?? true) + ) { + NProgress.start(); + } + } + + // 在开发环境中打印请求信息 + if (isDev) { + console.group(req.url); + console.log('method:', req.method); + console.table('data:', req.method == 'get' ? req.params : req.data); + console.groupEnd(); + } + + if (!req.headers) { + req.headers = {}; + } + + // 设置请求头中的语言 + if (req.headers['language'] !== null) { + req.headers['language'] = config.i18n.locale; + } + + // 验证 token + if (user.token) { + // 设置请求头中的 Authorization + if (req.headers['Authorization'] !== null) { + req.headers['Authorization'] = user.token; + } + + // 忽略特定请求 + if (['eps', 'refreshToken'].some(e => endsWith(req.url, e))) { + return req; + } + + // 判断 token 是否过期 + if (storage.isExpired('token')) { + // 判断 refreshToken 是否过期 + if (storage.isExpired('refreshToken')) { + ElMessage.error('登录状态已失效,请重新登录'); + user.logout(); + } else { + // 如果不在刷新中,则刷新 token + if (!isRefreshing) { + isRefreshing = true; + + user.refreshToken() + .then(token => { + queue.forEach(cb => cb(token)); // 处理队列中的请求 + queue = []; + isRefreshing = false; + }) + .catch(() => { + user.logout(); + }); + } + + // 返回一个新的 Promise,等待 token 刷新完成 + return new Promise(resolve => { + queue.push(token => { + if (req.headers) { + req.headers['Authorization'] = token; // 重新设置 token + } + resolve(req); + }); + }); + } + } + } + + return req; + }, + error => { + return Promise.reject(error); // 请求错误处理 + } +); + +// 响应拦截器 +request.interceptors.response.use( + res => { + NProgress.done(); // 结束进度条 + + if (!res?.data) { + return res; + } + + const { code, data, message } = res.data; + + if (!code) { + return res.data; // 返回数据 + } + + switch (code) { + case 1000: + return data; // 成功返回数据 + default: + return Promise.reject({ code, message }); // 处理错误 + } + }, + async error => { + NProgress.done(); // 结束进度条 + + if (error.response) { + const { status } = error.response; + const { user } = useBase(); + + if (status == 401) { + user.logout(); // 未授权,登出用户 + } else { + if (!isDev) { + switch (status) { + case 403: + router.push('/403'); // 禁止访问 + break; + + case 500: + router.push('/500'); // 服务器错误 + break; + + case 502: + router.push('/502'); // 网关错误 + break; + } + } + } + } + + return Promise.reject({ message: error.response?.data?.message || error.message }); // 返回错误信息 + } +); + +export { request }; diff --git a/cool-admin-vue/src/cool/service/stream.ts b/cool-admin-vue/src/cool/service/stream.ts new file mode 100644 index 0000000..c941719 --- /dev/null +++ b/cool-admin-vue/src/cool/service/stream.ts @@ -0,0 +1,103 @@ +import { useBase } from '/$/base'; +import { config } from '/@/config'; + +export function useStream() { + const { user } = useBase(); + let abortController: AbortController | null = null; + + // 调用 + async function invoke({ + url, + method = 'POST', + data, + cb + }: { + url: string; + method?: string; + data?: any; + cb?: (result: any) => void; + }) { + abortController = new AbortController(); + + let cacheText = ''; + + return fetch(config.baseUrl + url, { + method, + headers: { + Authorization: user.token, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data), + signal: abortController?.signal + }) + .then(res => { + if (res.body) { + const reader = res.body.getReader(); + const decoder = new TextDecoder('utf-8'); + const stream = new ReadableStream({ + start(controller) { + function push() { + reader.read().then(({ done, value }) => { + if (done) { + controller.close(); + return; + } + + let text = decoder.decode(value, { stream: true }); + + if (cb) { + if (cacheText) { + text = cacheText + text; + } + + if (text.indexOf('data:') == 0) { + text = '\n\n' + text; + } + + try { + const arr = text + .split(/\n\ndata:/g) + .filter(Boolean) + .map(e => JSON.parse(e)); + + arr.forEach(cb); + + cacheText = ''; + } catch (err) { + console.error('[parse text]', text); + cacheText = text; + } + } + + controller.enqueue(text); + push(); + }); + } + push(); + } + }); + + return new Response(stream); + } + + return res; + }) + .catch(err => { + console.error(err); + throw err; + }); + } + + // 取消 + function cancel() { + if (abortController) { + abortController.abort(); + abortController = null; + } + } + + return { + invoke, + cancel + }; +} diff --git a/cool-admin-vue/src/cool/types/index.ts b/cool-admin-vue/src/cool/types/index.ts new file mode 100644 index 0000000..8d9772e --- /dev/null +++ b/cool-admin-vue/src/cool/types/index.ts @@ -0,0 +1,60 @@ +import type { Component, Directive, App } from 'vue'; +import type { Router as VueRouter, RouteRecordRaw } from 'vue-router'; + +export declare type Merge = Omit & B; + +export declare interface ModuleConfig { + enable?: boolean; + name?: string; + label?: string; + description?: string; + order?: number; + version?: string; + logo?: string; + author?: string; + updateTime?: string; + demo?: { name: string; component: Component }[] | string; + doc?: string; + ignore?: { + NProgress?: string[]; + token?: string[]; + }; + options?: { + [key: string]: any; + }; + toolbar?: { + order?: number; + pc?: boolean; + h5?: boolean; + component: any; + }; + index?: { + component: any; + }; + components?: Component[]; + views?: RouteRecordRaw[]; + pages?: (RouteRecordRaw & { isPage?: boolean })[]; + install?(app: App, options?: any): any; + onLoad?(events: { + hasToken: (cb: () => Promise | void) => Promise | void; + [key: string]: any; + }): Promise<{ [key: string]: any }> | Promise | void; +} + +export declare interface Module extends ModuleConfig { + name: string; + options: { + [key: string]: any; + }; + value?: any; + services?: { path: string; value: any }[]; + directives?: { name: string; value: Directive }[]; + [key: string]: any; +} + +export declare interface Router extends VueRouter { + find(path: string): { route: RouteRecordRaw; isReg: boolean }; + del(name: string): void; + clear(): void; + append(data: any | any[]): void; +} diff --git a/cool-admin-vue/src/cool/utils/index.ts b/cool-admin-vue/src/cool/utils/index.ts new file mode 100644 index 0000000..8962b79 --- /dev/null +++ b/cool-admin-vue/src/cool/utils/index.ts @@ -0,0 +1,301 @@ +import { isArray, isNumber, isString, orderBy } from 'lodash-es'; +import { resolveComponent } from 'vue'; +import storage from './storage'; + +// 首字母大写 +export function firstUpperCase(value: string): string { + return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) { + return $1.toUpperCase() + $2; + }); +} + +// 获取方法名 +export function getNames(value: any) { + return Object.getOwnPropertyNames(value.constructor.prototype); +} + +// 获取地址栏参数 +export function getUrlParam(name: string): string | null { + const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); + const r = window.location.search.substr(1).match(reg); + if (r != null) return decodeURIComponent(r[2]); + return null; +} + +// 文件名 +export function filename(path: string): string { + return basename(path.substring(0, path.lastIndexOf('.'))); +} + +// 路径名称 +export function basename(path: string): string { + let index = path.lastIndexOf('/'); + index = index > -1 ? index : path.lastIndexOf('\\'); + if (index < 0) { + return path; + } + return path.substring(index + 1); +} + +// 文件扩展名 +export function extname(path: string): string { + return path.substring(path.lastIndexOf('.') + 1).split(/(\?|&)/)[0]; +} + +// 横杠转驼峰 +export function toCamel(str: string): string { + return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) { + return $1 + $2.toUpperCase(); + }); +} + +// uuid +export function uuid(separator = '-'): string { + const s: any[] = []; + const hexDigits = '0123456789abcdef'; + for (let i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = '4'; + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); + s[8] = s[13] = s[18] = s[23] = separator; + + return s.join(''); +} + +// 浏览器信息 +export function getBrowser() { + const { clientHeight, clientWidth } = document.documentElement; + + // 浏览器信息 + const ua = navigator.userAgent.toLowerCase(); + + // 浏览器类型 + let type = (ua.match(/firefox|chrome|safari|opera/g) || 'other')[0]; + + if ((ua.match(/msie|trident/g) || [])[0]) { + type = 'msie'; + } + + // 平台标签 + let tag = ''; + + const isTocuh = + 'ontouchstart' in window || ua.indexOf('touch') !== -1 || ua.indexOf('mobile') !== -1; + if (isTocuh) { + if (ua.indexOf('ipad') !== -1) { + tag = 'pad'; + } else if (ua.indexOf('mobile') !== -1) { + tag = 'mobile'; + } else if (ua.indexOf('android') !== -1) { + tag = 'androidPad'; + } else { + tag = 'pc'; + } + } else { + tag = 'pc'; + } + + // 浏览器内核 + let prefix = ''; + + switch (type) { + case 'chrome': + case 'safari': + case 'mobile': + prefix = 'webkit'; + break; + case 'msie': + prefix = 'ms'; + break; + case 'firefox': + prefix = 'Moz'; + break; + case 'opera': + prefix = 'O'; + break; + default: + prefix = 'webkit'; + break; + } + + // 操作平台 + const plat = ua.indexOf('android') > 0 ? 'android' : navigator.platform.toLowerCase(); + + // 屏幕信息 + let screen = 'full'; + + if (clientWidth < 768) { + screen = 'xs'; + } else if (clientWidth < 992) { + screen = 'sm'; + } else if (clientWidth < 1200) { + screen = 'md'; + } else if (clientWidth < 1920) { + screen = 'xl'; + } else { + screen = 'full'; + } + + // 是否 ios + const isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); + + // 是否 PC 端 + const isPC = tag === 'pc'; + + // 是否移动端 + const isMobile = isPC ? false : true; + + // 是否移动端 + 屏幕宽过小 + const isMini = screen === 'xs' || isMobile; + + return { + height: clientHeight, + width: clientWidth, + type, + plat, + tag, + prefix, + isMobile, + isIOS, + isPC, + isMini, + screen + }; +} + +// 路径转数组 +export function deepPaths(paths: string[], splitor?: string) { + const list: any[] = []; + + paths.forEach(e => { + const arr: string[] = e.split(splitor || '/').filter(Boolean); + + let c = list; + + arr.forEach((a, i) => { + let d = c.find(e => e.label == a); + + if (!d) { + d = { + label: a, + value: a, + children: arr[i + 1] ? [] : null + }; + + c.push(d); + } + + if (d.children) { + c = d.children; + } + }); + }); + + return list; +} + +// 列表转树形 +export function deepTree(list: any[], sort?: 'desc' | 'asc'): any[] { + const newList: any[] = []; + const map: any = {}; + + orderBy(list, 'orderNum', sort) + .map(e => { + map[e.id] = e; + return e; + }) + .forEach(e => { + const parent = map[e.parentId]; + + if (parent) { + (parent.children || (parent.children = [])).push(e); + } else { + newList.push(e); + } + }); + + return newList; +} + +// 树形转列表 +export function revDeepTree(list: any[]) { + const arr: any[] = []; + let id = 0; + + function deep(list: any[], parentId: number) { + list.forEach(e => { + if (!e.id) { + e.id = ++id; + } + + if (!e.parentId) { + e.parentId = parentId; + } + + arr.push(e); + + if (e.children && isArray(e.children) && e.id) { + deep(e.children, e.id); + } + }); + } + + deep(list || [], 0); + + return arr; +} + +// 路径转对象 +export function path2Obj(list: any[]) { + const data: any = {}; + + list.forEach(({ path, value }) => { + if (path) { + const arr: string[] = path.split('/'); + const parents = arr.slice(0, arr.length - 1); + const name = basename(path).replace('.ts', ''); + + let curr = data; + + parents.forEach(k => { + if (!curr[k]) { + curr[k] = {}; + } + + curr = curr[k]; + }); + + curr[name] = value; + } + }); + + return data; +} + +// 是否是组件 +export function isComponent(name: string) { + return !isString(resolveComponent(name)); +} + +// 是否Promise +export function isPromise(val: any) { + return val && Object.prototype.toString.call(val) === '[object Promise]'; +} + +// 单位转换 +export function parsePx(val: string | number) { + return isNumber(val) ? `${val}px` : val; +} + +// 延迟 +export function sleep(duration: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve(true); + }, duration); + }); +} + +export { storage }; +export * from './loading'; diff --git a/cool-admin-vue/src/cool/utils/loading.ts b/cool-admin-vue/src/cool/utils/loading.ts new file mode 100644 index 0000000..ac552ff --- /dev/null +++ b/cool-admin-vue/src/cool/utils/loading.ts @@ -0,0 +1,37 @@ +export const Loading = { + resolve: null as (() => void) | null, + next: null as Promise | null, + + async set(list: Promise[]) { + try { + await Promise.all(list); + } catch (e) { + console.error('[Loading] Error: ', e); + } + + if (this.resolve) { + this.resolve(); + } + }, + + async wait() { + if (this.next) { + return this.next; + } + return Promise.resolve(); + }, + + close() { + const el = document.getElementById('Loading'); + + if (el) { + setTimeout(() => { + el.classList.add('is-hide'); + }, 0); + } + } +}; + +Loading.next = new Promise(resolve => { + Loading.resolve = resolve; +}); diff --git a/cool-admin-vue/src/cool/utils/storage.ts b/cool-admin-vue/src/cool/utils/storage.ts new file mode 100644 index 0000000..1763c6e --- /dev/null +++ b/cool-admin-vue/src/cool/utils/storage.ts @@ -0,0 +1,83 @@ +import store from 'store'; + +export default { + // 后缀标识 + suffix: '_deadtime', + + /** + * 获取 + * @param {string} key 关键字 + */ + get(key: string) { + return store.get(key); + }, + + /** + * 获取全部 + */ + info() { + const data: Record = {}; + + store.each((value: any, key: any) => { + data[key] = value; + }); + + return data; + }, + + /** + * 设置 + * @param {string} key 关键字 + * @param {*} value 值 + * @param {number} expires 过期时间 + */ + set(key: string, value: any, expires?: number) { + store.set(key, value); + + if (expires) { + const expirationTime = Date.now() + expires * 1000; + store.set(`${key}${this.suffix}`, expirationTime); + } + }, + + /** + * 是否过期 + * @param {string} key 关键字 + */ + isExpired(key: string) { + const expiration = this.getExpiration(key) || 0; + return expiration - Date.now() <= 2000; + }, + + /** + * 获取到期时间 + * @param {string} key 关键字 + */ + getExpiration(key: string) { + return this.get(key + this.suffix); + }, + + /** + * 移除 + * @param {string} key 关键字 + */ + remove(key: string) { + store.remove(key); + this.removeExpiration(key); + }, + + /** + * 移除到期时间 + * @param {string} key 关键字 + */ + removeExpiration(key: string) { + store.remove(key + this.suffix); + }, + + /** + * 清理 + */ + clearAll() { + store.clearAll(); + } +}; diff --git a/cool-admin-vue/src/main.ts b/cool-admin-vue/src/main.ts new file mode 100644 index 0000000..870bc82 --- /dev/null +++ b/cool-admin-vue/src/main.ts @@ -0,0 +1,14 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import { bootstrap } from './cool'; + +const app = createApp(App); + +// 启动 +bootstrap(app) + .then(() => { + app.mount('#app'); + }) + .catch(err => { + console.error('COOL-ADMIN 启动失败', err); + }); diff --git a/cool-admin-vue/src/modules/base/components/avatar/index.tsx b/cool-admin-vue/src/modules/base/components/avatar/index.tsx new file mode 100644 index 0000000..8e38926 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/avatar/index.tsx @@ -0,0 +1,53 @@ +import { defineComponent, type PropType } from 'vue'; +import { UserFilled } from '@element-plus/icons-vue'; + +export default defineComponent({ + name: 'cl-avatar', + + props: { + modelValue: String, + src: String, + icon: { + type: null, + default: UserFilled + }, + size: { + type: [String, Number] as PropType<'large' | 'default' | 'small' | number>, + default: 40 + }, + shape: { + type: String as PropType<'circle' | 'square'>, + default: 'square' + }, + fit: { + type: String as PropType<'fill' | 'contain' | 'cover' | 'none' | 'scale-down'>, + default: 'cover' + } + }, + + setup(props) { + return () => { + const height = props.size + 'px'; + + return ( +
+ +
+ ); + }; + } +}); diff --git a/cool-admin-vue/src/modules/base/components/code/json.vue b/cool-admin-vue/src/modules/base/components/code/json.vue new file mode 100644 index 0000000..2cd15f8 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/code/json.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/dept/check.vue b/cool-admin-vue/src/modules/base/components/dept/check.vue new file mode 100644 index 0000000..acfe65e --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/dept/check.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/dept/select.vue b/cool-admin-vue/src/modules/base/components/dept/select.vue new file mode 100644 index 0000000..b1a02ba --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/dept/select.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/editor/index.tsx b/cool-admin-vue/src/modules/base/components/editor/index.tsx new file mode 100644 index 0000000..d4059a9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/editor/index.tsx @@ -0,0 +1,44 @@ +import { defineComponent, h, resolveComponent, ref, reactive, watch } from 'vue'; +import { isComponent } from '/@/cool/utils'; +import { assign } from 'lodash-es'; +import { useI18n } from 'vue-i18n'; + +export default defineComponent({ + name: 'cl-editor', + + props: { + name: { + type: String, + required: true + } + }, + + setup(props, { slots, expose }) { + const Editor = ref(); + const ex = reactive({}); + const { t } = useI18n(); + + watch(Editor, v => { + if (v) { + assign(ex, v); + } + }); + + expose(ex); + + return () => { + return isComponent(props.name) ? ( + h( + resolveComponent(props.name), + { + ...props, + ref: Editor + }, + slots + ) + ) : ( + + ); + }; + } +}); diff --git a/cool-admin-vue/src/modules/base/components/icon/svg.vue b/cool-admin-vue/src/modules/base/components/icon/svg.vue new file mode 100644 index 0000000..3bee493 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/icon/svg.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/image/index.vue b/cool-admin-vue/src/modules/base/components/image/index.vue new file mode 100644 index 0000000..cc36494 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/image/index.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/link/index.vue b/cool-admin-vue/src/modules/base/components/link/index.vue new file mode 100644 index 0000000..1aa496a --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/link/index.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/menu/check.vue b/cool-admin-vue/src/modules/base/components/menu/check.vue new file mode 100644 index 0000000..12f9d93 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/menu/check.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/menu/file.vue b/cool-admin-vue/src/modules/base/components/menu/file.vue new file mode 100644 index 0000000..597f398 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/menu/file.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/menu/icon.vue b/cool-admin-vue/src/modules/base/components/menu/icon.vue new file mode 100644 index 0000000..02d5b3f --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/menu/icon.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/menu/perms.vue b/cool-admin-vue/src/modules/base/components/menu/perms.vue new file mode 100644 index 0000000..ee043e9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/menu/perms.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/menu/select.vue b/cool-admin-vue/src/modules/base/components/menu/select.vue new file mode 100644 index 0000000..86c8435 --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/menu/select.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/components/num/index.vue b/cool-admin-vue/src/modules/base/components/num/index.vue new file mode 100644 index 0000000..017d2ed --- /dev/null +++ b/cool-admin-vue/src/modules/base/components/num/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/config.ts b/cool-admin-vue/src/modules/base/config.ts new file mode 100644 index 0000000..cac5e6f --- /dev/null +++ b/cool-admin-vue/src/modules/base/config.ts @@ -0,0 +1,94 @@ +import { type ModuleConfig } from '/@/cool'; +import { useStore } from './store'; +import { config } from '/@/config'; +import { t } from '/@/plugins/i18n'; +import './static/css/index.scss'; + +export default (): ModuleConfig => { + return { + order: 99, + ignore: { + NProgress: [ + '/base/open/eps', + '/base/comm/person', + '/base/comm/permmenu', + '/base/comm/upload', + '/base/comm/uploadMode' + ], + token: ['/login', '/401', '/403', '/404', '/500', '/502'] + }, + components: Object.values(import.meta.glob('./components/**/*.{vue,tsx}')), + views: [ + { + path: '/my/info', + meta: { + label: t('个人中心') + }, + component: () => import('./views/info.vue') + } + ], + pages: [ + { + path: '/login', + component: () => import('./pages/login/index.vue') + }, + ...['401', '403', '404', '500', '502'].map(code => { + return { + path: `/${code}`, + meta: { + process: false + }, + component: () => import(`./pages/error/${code}.vue`) + }; + }) + ], + install() { + // 设置标题 + document.title = config.app.name; + + // 设置加载文案 + const loading = document.querySelector('#Loading'); + + if (loading) { + const name = loading.querySelector('.preload__name'); + const title = loading.querySelector('.preload__title'); + const subTitle = loading.querySelector('.preload__sub-title'); + + if (name) { + name.innerHTML = config.app.name; + } + if (title) { + title.innerHTML = t('正在加载资源...'); + } + if (subTitle) { + subTitle.innerHTML = t('初次加载资源可能需要较多时间,请耐心等待'); + } + } + }, + async onLoad() { + const { user, menu, app } = useStore(); + + // token 事件 + async function hasToken(cb: () => Promise | void) { + if (cb) { + app.addEvent('hasToken', cb); + + if (user.token) { + await cb(); + } + } + } + + await hasToken(async () => { + // 获取用户信息 + user.get(); + // 获取菜单权限 + await menu.get(); + }); + + return { + hasToken + }; + } + }; +}; diff --git a/cool-admin-vue/src/modules/base/directives/permission.ts b/cool-admin-vue/src/modules/base/directives/permission.ts new file mode 100644 index 0000000..969863a --- /dev/null +++ b/cool-admin-vue/src/modules/base/directives/permission.ts @@ -0,0 +1,13 @@ +import { checkPerm } from '../utils/permission'; + +function change(el: HTMLElement, binding: { value: any }) { + el.style.display = checkPerm(binding.value) ? el.getAttribute('_display') || '' : 'none'; +} + +export default { + created(el: HTMLElement, binding: { value: any }) { + el.setAttribute('_display', el.style.display || ''); + change(el, binding); + }, + updated: change +}; diff --git a/cool-admin-vue/src/modules/base/index.ts b/cool-admin-vue/src/modules/base/index.ts new file mode 100644 index 0000000..b09f19d --- /dev/null +++ b/cool-admin-vue/src/modules/base/index.ts @@ -0,0 +1,9 @@ +import { useStore } from './store'; + +export function useBase() { + return { + ...useStore() + }; +} + +export * from './utils'; diff --git a/cool-admin-vue/src/modules/base/locales/en.json b/cool-admin-vue/src/modules/base/locales/en.json new file mode 100644 index 0000000..f48f186 --- /dev/null +++ b/cool-admin-vue/src/modules/base/locales/en.json @@ -0,0 +1,143 @@ +{ + "删除": "Delete", + "新增成员": "Add Member", + "目录": "Directory", + "菜单": "Menu", + "权限": "Permission", + "是否显示": "Show/Hide", + "图标": "Icon", + "节点路由": "Node Route", + "路由缓存": "Route Cache", + "文件路径": "File Path", + "排序号": "Sorting Number", + "节点类型": "Node Type", + "节点名称": "Node Name", + "上级节点": "Parent Node", + "请输入节点路由,如:/test": "Please enter the node route, e.g.: /test", + "开启": "Enable", + "关闭": "Disable", + "请填写排序号": "Please fill in the sorting number", + "导入": "Import", + "如遇到问题无法导入菜单,请检查文件并尝试重新导入。": "If you encounter problems importing the menu, please check the file and try to import again.", + "角色标签": "Character Tag", + "请填写新密码": "Please fill in the new password", + "保存修改": "Save Changes", + "修改成功": "Modification Successful", + "拼命加载中": "Loading拼命", + "转移": "Transfer", + "搜索用户名、姓名": "Search Username, Name", + "用户列表": "User List", + "用户名": "Username", + "姓名": "Name", + "部门名称": "Department Name", + "角色": "Role", + "状态": "Status", + "手机号码": "Mobile Phone Number", + "选择头像": "Select Avatar", + "密码": "Password", + "密码长度在 6 到 16 个字符": "Password length should be between 6 and 16 characters", + "邮箱": "Email", + "启用": "Enable", + "禁用": "Disable", + "部门转移": "Department Transfer", + "请输入备注": "Please enter remarks", + "清空": "Clear", + "日志保存天数": "Log save days", + "搜索请求地址、用户昵称、ip": "Search request address, user nickname, IP", + "用户ID": "User ID", + "用户昵称": "User nickname", + "请求地址": "Request address", + "参数": "Parameter", + "请求时间": "Request time", + "保存成功": "Save successful", + "是否要清空日志?": "Do you want to clear the log?", + "提示": "Tip", + "清空成功": "Clear successful", + "基本信息": "Basic information", + "头像": "Avatar", + "昵称": "Nickname", + "请填写昵称": "Please fill in the nickname", + "原密码": "Original password", + "请填写原密码": "Please fill in the original password", + "新密码": "New password", + "菜单导入": "Menu Import", + "添加": "Add", + "导入成功": "Import Success", + "{file}文件格式错误:{error}": "{file} File Format Error: {error}", + "导出": "Export", + "选择菜单": "Select Menu", + "请先选择要导出的菜单": "Please select the menu to export first", + "菜单数据": "Menu Data", + "退出登录": "Log out", + "确定退出登录吗?": "Are you sure you want to log out?", + "搜索关键字": "Search Keyword", + "关闭当前": "Close Current", + "关闭其他": "Close Others", + "关闭所有": "Close All", + "{label} 没有子菜单,请先添加": "{label} has no sub-menus. Please add them first", + "快速开发后台权限管理系统": "Quick Development Background Permission Management System", + "请输入用户名": "Please enter your username", + "请输入密码": "Please enter your password", + "验证码": "Verification Code", + "登录": "Log in", + "用户名不能为空": "Username cannot be empty", + "密码不能为空": "Password cannot be empty", + "图片验证码不能为空": "Image verification code cannot be empty", + "验证码获取失败": "Failed to obtain verification code", + "马上回来": "Be right back", + "糟糕,出了点问题": "Oops, something went wrong", + "找不到您要查找的页面": "Page not found", + "您无权访问此页面": "You are not authorized to access this page", + "认证失败,请重新登录!": "Authentication failed, please log in again!", + "返回首页": "Return to home page", + "重新登录": "Log in again", + "返回登录页": "Return to login page", + "自定义输入": "Custom input", + "请输入": "Please enter", + "输入关键字进行过滤": "Enter keywords for filtering", + "复制": "Copy", + "行为": "Behavior", + "ip": "IP", + "数据类型 0-字符串 1-富文本 2-文件 ": "Data type 0 - String 1 - Rich text 2 - File", + "键": "Key", + "选择部门": "Select Department", + "请选择部门": "Please Select Department", + "转移到新部门,是否继续?": "Transfer to a new department. Continue?", + "转移成功": "Transfer Successful", + "组织架构": "Organization Structure", + "刷新": "Refresh", + "拖动排序": "Drag to Sort", + "编辑部门": "Edit Department", + "上级部门": "Superior Department", + "排序": "Sort", + "新增部门 “{name}” 成功": "Successfully added new department “{name}”", + "删除成功": "Delete Successful", + "“{name}” 部门的用户已成功转移到 “{parentName}” 部门。": "Users in the “{name}” department have been successfully transferred to the “{parentName}” department.", + "此操作将会删除 “{name}” 部门的所有用户,是否确认?": "This operation will delete all users in the “{name}” department. Are you sure?", + "直接删除": "Delete Directly", + "保留用户": "Keep Users", + "部门架构已发生改变,是否保存?": "The department structure has changed. Do you want to save?", + "更新排序成功": "Successfully updated sorting", + "新增": "Add", + "编辑": "Edit", + "个人中心": "Personal Center", + "正在加载资源...": "Loading resources...", + "初次加载资源可能需要较多时间,请耐心等待": "It may take some time for the initial resource loading. Please wait patiently.", + "搜索名称": "Search Name", + "是否关联上下级": "Whether to associate with superiors and subordinates", + "名称": "Name", + "标识": "Identifier", + "备注": "Remarks", + "功能权限": "Function Permissions", + "数据权限": "Data Permissions", + "创建时间": "Creation Time", + "更新时间": "Update Time", + "数据类型": "Data Type", + "搜索名称、keyName": "Search Name, keyName", + "字符串": "String", + "富文本": "Rich Text", + "文件": "File", + "请输入Key": "Please enter Key", + "类型": "Type", + "数据": "Data" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/locales/zh-cn.json b/cool-admin-vue/src/modules/base/locales/zh-cn.json new file mode 100644 index 0000000..ac9bfdf --- /dev/null +++ b/cool-admin-vue/src/modules/base/locales/zh-cn.json @@ -0,0 +1,143 @@ +{ + "个人中心": "个人中心", + "正在加载资源...": "正在加载资源...", + "初次加载资源可能需要较多时间,请耐心等待": "初次加载资源可能需要较多时间,请耐心等待", + "搜索名称": "搜索名称", + "是否关联上下级": "是否关联上下级", + "名称": "名称", + "标识": "标识", + "备注": "备注", + "功能权限": "功能权限", + "数据权限": "数据权限", + "创建时间": "创建时间", + "更新时间": "更新时间", + "数据类型": "数据类型", + "搜索名称、keyName": "搜索名称、keyName", + "字符串": "字符串", + "富文本": "富文本", + "文件": "文件", + "请输入Key": "请输入Key", + "类型": "类型", + "数据": "数据", + "请输入备注": "请输入备注", + "清空": "清空", + "日志保存天数": "日志保存天数", + "搜索请求地址、用户昵称、ip": "搜索请求地址、用户昵称、ip", + "用户ID": "用户ID", + "用户昵称": "用户昵称", + "请求地址": "请求地址", + "参数": "参数", + "请求时间": "请求时间", + "保存成功": "保存成功", + "是否要清空日志?": "是否要清空日志?", + "提示": "提示", + "清空成功": "清空成功", + "基本信息": "基本信息", + "头像": "头像", + "昵称": "昵称", + "请填写昵称": "请填写昵称", + "原密码": "原密码", + "请填写原密码": "请填写原密码", + "新密码": "新密码", + "请填写新密码": "请填写新密码", + "保存修改": "保存修改", + "修改成功": "修改成功", + "拼命加载中": "拼命加载中", + "转移": "转移", + "搜索用户名、姓名": "搜索用户名、姓名", + "用户列表": "用户列表", + "用户名": "用户名", + "姓名": "姓名", + "部门名称": "部门名称", + "角色": "角色", + "状态": "状态", + "手机号码": "手机号码", + "选择头像": "选择头像", + "密码": "密码", + "密码长度在 6 到 16 个字符": "密码长度在 6 到 16 个字符", + "邮箱": "邮箱", + "启用": "启用", + "禁用": "禁用", + "部门转移": "部门转移", + "选择部门": "选择部门", + "请选择部门": "请选择部门", + "转移到新部门,是否继续?": "转移到新部门,是否继续?", + "转移成功": "转移成功", + "组织架构": "组织架构", + "刷新": "刷新", + "拖动排序": "拖动排序", + "编辑部门": "编辑部门", + "上级部门": "上级部门", + "排序": "排序", + "新增部门 “{name}” 成功": "新增部门 “{name}” 成功", + "删除成功": "删除成功", + "“{name}” 部门的用户已成功转移到 “{parentName}” 部门。": "“{name}” 部门的用户已成功转移到 “{parentName}” 部门。", + "此操作将会删除 “{name}” 部门的所有用户,是否确认?": "此操作将会删除 “{name}” 部门的所有用户,是否确认?", + "直接删除": "直接删除", + "保留用户": "保留用户", + "部门架构已发生改变,是否保存?": "部门架构已发生改变,是否保存?", + "更新排序成功": "更新排序成功", + "新增": "新增", + "编辑": "编辑", + "删除": "删除", + "新增成员": "新增成员", + "目录": "目录", + "菜单": "菜单", + "权限": "权限", + "是否显示": "是否显示", + "图标": "图标", + "节点路由": "节点路由", + "路由缓存": "路由缓存", + "文件路径": "文件路径", + "排序号": "排序号", + "节点类型": "节点类型", + "节点名称": "节点名称", + "上级节点": "上级节点", + "请输入节点路由,如:/test": "请输入节点路由,如:/test", + "开启": "开启", + "关闭": "关闭", + "请填写排序号": "请填写排序号", + "导入": "导入", + "如遇到问题无法导入菜单,请检查文件并尝试重新导入。": "如遇到问题无法导入菜单,请检查文件并尝试重新导入。", + "菜单导入": "菜单导入", + "添加": "添加", + "导入成功": "导入成功", + "{file}文件格式错误:{error}": "{file}文件格式错误:{error}", + "导出": "导出", + "选择菜单": "选择菜单", + "请先选择要导出的菜单": "请先选择要导出的菜单", + "菜单数据": "菜单数据", + "退出登录": "退出登录", + "确定退出登录吗?": "确定退出登录吗?", + "搜索关键字": "搜索关键字", + "关闭当前": "关闭当前", + "关闭其他": "关闭其他", + "关闭所有": "关闭所有", + "{label} 没有子菜单,请先添加": "{label} 没有子菜单,请先添加", + "快速开发后台权限管理系统": "快速开发后台权限管理系统", + "请输入用户名": "请输入用户名", + "请输入密码": "请输入密码", + "验证码": "验证码", + "登录": "登录", + "用户名不能为空": "用户名不能为空", + "密码不能为空": "密码不能为空", + "图片验证码不能为空": "图片验证码不能为空", + "验证码获取失败": "验证码获取失败", + "马上回来": "马上回来", + "糟糕,出了点问题": "糟糕,出了点问题", + "找不到您要查找的页面": "找不到您要查找的页面", + "您无权访问此页面": "您无权访问此页面", + "认证失败,请重新登录!": "认证失败,请重新登录!", + "返回首页": "返回首页", + "重新登录": "重新登录", + "返回登录页": "返回登录页", + "自定义输入": "自定义输入", + "请输入": "请输入", + "输入关键字进行过滤": "输入关键字进行过滤", + "复制": "复制", + "行为": "行为", + "ip": "ip", + "数据类型 0-字符串 1-富文本 2-文件 ": "数据类型 0-字符串 1-富文本 2-文件 ", + "键": "键", + "角色标签": "角色标签" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/locales/zh-tw.json b/cool-admin-vue/src/modules/base/locales/zh-tw.json new file mode 100644 index 0000000..867fe59 --- /dev/null +++ b/cool-admin-vue/src/modules/base/locales/zh-tw.json @@ -0,0 +1,143 @@ +{ + "请填写新密码": "請填寫新密碼", + "保存修改": "保存修改", + "修改成功": "修改成功", + "拼命加载中": "拼命加載中", + "转移": "轉移", + "搜索用户名、姓名": "搜索用戶名、姓名", + "用户列表": "用戶列表", + "用户名": "用戶名", + "姓名": "姓名", + "部门名称": "部門名稱", + "角色": "角色", + "状态": "狀態", + "手机号码": "手機號碼", + "选择头像": "選擇頭像", + "密码": "密碼", + "密码长度在 6 到 16 个字符": "密碼長度在6到16個字符", + "邮箱": "郵箱", + "启用": "啟用", + "禁用": "禁用", + "部门转移": "部門轉移", + "请输入备注": "請輸入備註", + "清空": "清空", + "日志保存天数": "日誌保存天數", + "搜索请求地址、用户昵称、ip": "搜索請求地址、用戶暱稱、ip", + "用户ID": "用戶ID", + "用户昵称": "用戶暱稱", + "请求地址": "請求地址", + "参数": "參數", + "请求时间": "請求時間", + "保存成功": "保存成功", + "是否要清空日志?": "是否要清空日誌?", + "提示": "提示", + "清空成功": "清空成功", + "基本信息": "基本信息", + "头像": "頭像", + "昵称": "暱稱", + "请填写昵称": "請填寫暱稱", + "原密码": "原密碼", + "请填写原密码": "請填寫原密碼", + "新密码": "新密碼", + "角色标签": "角色標籤", + "个人中心": "個人中心", + "正在加载资源...": "正在加載資源...", + "初次加载资源可能需要较多时间,请耐心等待": "初次加載資源可能需要較多時間,請耐心等待", + "搜索名称": "搜索名稱", + "是否关联上下级": "是否關聯上下級", + "名称": "名稱", + "标识": "標識", + "备注": "備註", + "功能权限": "功能權限", + "数据权限": "數據權限", + "创建时间": "創建時間", + "更新时间": "更新時間", + "数据类型": "數據類型", + "搜索名称、keyName": "搜索名稱、keyName", + "字符串": "字串", + "富文本": "富文本", + "文件": "文件", + "请输入Key": "請輸入Key", + "类型": "類型", + "数据": "數據", + "删除": "刪除", + "新增成员": "新增成員", + "目录": "目錄", + "菜单": "菜單", + "权限": "權限", + "是否显示": "是否顯示", + "图标": "圖標", + "节点路由": "節點路由", + "路由缓存": "路由緩存", + "文件路径": "文件路徑", + "排序号": "排序號", + "节点类型": "節點類型", + "节点名称": "節點名稱", + "上级节点": "上級節點", + "请输入节点路由,如:/test": "請輸入節點路由,如:/test", + "开启": "開啟", + "关闭": "關閉", + "请填写排序号": "請填寫排序號", + "导入": "導入", + "如遇到问题无法导入菜单,请检查文件并尝试重新导入。": "如遇到問題無法導入菜單,請檢查文件並嘗試重新導入。", + "用户名不能为空": "用戶名不得為空", + "密码不能为空": "密碼不得為空", + "图片验证码不能为空": "圖片驗證碼不得為空", + "验证码获取失败": "驗證碼獲取失敗", + "马上回来": "馬上回來", + "糟糕,出了点问题": "糟糕,出了點問題", + "找不到您要查找的页面": "找不到您要查找的頁面", + "您无权访问此页面": "您無權訪問此頁面", + "认证失败,请重新登录!": "認證失敗,請重新登錄!", + "返回首页": "返回首頁", + "重新登录": "重新登錄", + "返回登录页": "返回登錄頁", + "自定义输入": "自定義輸入", + "请输入": "請輸入", + "输入关键字进行过滤": "輸入關鍵字進行過濾", + "复制": "複製", + "行为": "行為", + "ip": "IP", + "数据类型 0-字符串 1-富文本 2-文件 ": "數據類型 0-字串 1-富文本 2-文件 ", + "键": "鍵", + "菜单导入": "菜單導入", + "添加": "添加", + "导入成功": "導入成功", + "{file}文件格式错误:{error}": "{file}文件格式錯誤:{error}", + "导出": "導出", + "选择菜单": "選擇菜單", + "请先选择要导出的菜单": "請先選擇要導出的菜單", + "菜单数据": "菜單數據", + "退出登录": "退出登錄", + "确定退出登录吗?": "確定退出登錄嗎?", + "搜索关键字": "搜索關鍵字", + "关闭当前": "關閉當前", + "关闭其他": "關閉其他", + "关闭所有": "關閉所有", + "{label} 没有子菜单,请先添加": "{label} 沒有子菜單,請先添加", + "快速开发后台权限管理系统": "快速開發後台權限管理系統", + "请输入用户名": "請輸入用戶名", + "请输入密码": "請輸入密碼", + "验证码": "驗證碼", + "登录": "登錄", + "选择部门": "選擇部門", + "请选择部门": "請選擇部門", + "转移到新部门,是否继续?": "轉移到新部門,是否繼續?", + "转移成功": "轉移成功", + "组织架构": "組織架構", + "刷新": "刷新", + "拖动排序": "拖動排序", + "编辑部门": "編輯部門", + "上级部门": "上級部門", + "排序": "排序", + "新增部门 “{name}” 成功": "新增部門 “{name}” 成功", + "删除成功": "刪除成功", + "“{name}” 部门的用户已成功转移到 “{parentName}” 部门。": "“{name}” 部門的用戶已成功轉移到 “{parentName}” 部門。", + "此操作将会删除 “{name}” 部门的所有用户,是否确认?": "此操作將會刪除 “{name}” 部門的所有用戶,是否確認?", + "直接删除": "直接刪除", + "保留用户": "保留用戶", + "部门架构已发生改变,是否保存?": "部門架構已發生改變,是否保存?", + "更新排序成功": "更新排序成功", + "新增": "新增", + "编辑": "編輯" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/pages/error/401.vue b/cool-admin-vue/src/modules/base/pages/error/401.vue new file mode 100644 index 0000000..d2a6108 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/401.vue @@ -0,0 +1,11 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/error/403.vue b/cool-admin-vue/src/modules/base/pages/error/403.vue new file mode 100644 index 0000000..179617a --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/403.vue @@ -0,0 +1,11 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/error/404.vue b/cool-admin-vue/src/modules/base/pages/error/404.vue new file mode 100644 index 0000000..a3e1de1 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/404.vue @@ -0,0 +1,11 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/error/500.vue b/cool-admin-vue/src/modules/base/pages/error/500.vue new file mode 100644 index 0000000..e067e2f --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/500.vue @@ -0,0 +1,11 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/error/502.vue b/cool-admin-vue/src/modules/base/pages/error/502.vue new file mode 100644 index 0000000..d9680d7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/502.vue @@ -0,0 +1,11 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/error/components/error-page.vue b/cool-admin-vue/src/modules/base/pages/error/components/error-page.vue new file mode 100644 index 0000000..bfb7527 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/error/components/error-page.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/login/components/pic-captcha.vue b/cool-admin-vue/src/modules/base/pages/login/components/pic-captcha.vue new file mode 100644 index 0000000..87af631 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/login/components/pic-captcha.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/login/index.vue b/cool-admin-vue/src/modules/base/pages/login/index.vue new file mode 100644 index 0000000..2a235b8 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/login/index.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/login/static/bg.svg b/cool-admin-vue/src/modules/base/pages/login/static/bg.svg new file mode 100644 index 0000000..a96530d --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/login/static/bg.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/amenu.vue b/cool-admin-vue/src/modules/base/pages/main/components/amenu.vue new file mode 100644 index 0000000..b34000d --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/amenu.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/bmenu.tsx b/cool-admin-vue/src/modules/base/pages/main/components/bmenu.tsx new file mode 100644 index 0000000..6f0be72 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/bmenu.tsx @@ -0,0 +1,153 @@ +import { defineComponent, h, watch } from 'vue'; +import { useBase } from '/$/base'; +import { useCool } from '/@/cool'; +import { debounce } from 'lodash-es'; + +export default defineComponent({ + name: 'b-menu', + + props: { + keyWord: String + }, + + setup(props) { + const { router, route, browser, refs, setRefs } = useCool(); + const { menu, app } = useBase(); + + // 页面跳转 + function onSelect(url: string) { + if (url != route.path) { + router.push(url); + } + + // 小屏下点击收起左侧菜单 + if (browser.isMini) { + app.fold(true); + } + } + + // 渲染子菜单 + function renderMenu() { + function deep(list: Menu.Item[], show?: boolean) { + const keyWord = props.keyWord?.toLowerCase() || ''; + + function filterMenu(item: Menu.Item): boolean { + if (!item.isShow) return false; + + if (show) { + return true; + } + + if (item.meta?.label?.toLowerCase().includes(keyWord)) return true; + + if (item.children) { + return item.children.some(filterMenu); + } + + return false; + } + + return list.filter(filterMenu).map(e => { + if (e.meta?.label?.toLowerCase().includes(keyWord)) { + show = true; + } + + const item = (e: Menu.Item) => { + const arr = [ + , + + {e.meta?.label} + + ]; + + if (e.type == 1 && e.badge) { + arr.push( +
+ {e.badge} +
+ ); + } + return arr; + }; + + if (e.type == 0) { + return h( + , + { + index: String(e.id), + key: e.id, + popperClass: 'app-slider__menu' + }, + { + title() { + return item(e); + }, + default() { + return deep(e.children || [], show); + } + } + ); + } else { + return h( + , + { + index: e.meta?.isHome ? '/' : e.path, + key: e.id + }, + { + default() { + return item(e); + } + } + ); + } + }); + } + + return deep(menu.list); + } + + // 展开所有 + const expand = debounce(() => { + if (!props.keyWord) { + return; + } + + const deep = (list: Menu.Item[]) => { + list.forEach(e => { + if (e.type == 0) { + try { + refs.menu?.open(String(e.id)); + } catch (err) { } + + if (e.children) { + deep(e.children); + } + } + }); + }; + + deep(menu.list); + }, 300); + + watch(() => props.keyWord, expand); + + return () => { + return ( +
+ + {renderMenu()} + +
+ ); + }; + } +}); diff --git a/cool-admin-vue/src/modules/base/pages/main/components/global.vue b/cool-admin-vue/src/modules/base/pages/main/components/global.vue new file mode 100644 index 0000000..7134814 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/global.vue @@ -0,0 +1,37 @@ + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/process.vue b/cool-admin-vue/src/modules/base/pages/main/components/process.vue new file mode 100644 index 0000000..cf82f72 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/process.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/route-nav.vue b/cool-admin-vue/src/modules/base/pages/main/components/route-nav.vue new file mode 100644 index 0000000..54b9bfb --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/route-nav.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/slider.vue b/cool-admin-vue/src/modules/base/pages/main/components/slider.vue new file mode 100644 index 0000000..635262c --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/slider.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/topbar.vue b/cool-admin-vue/src/modules/base/pages/main/components/topbar.vue new file mode 100644 index 0000000..5559d13 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/topbar.vue @@ -0,0 +1,219 @@ + + + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/components/views.vue b/cool-admin-vue/src/modules/base/pages/main/components/views.vue new file mode 100644 index 0000000..0ae0219 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/components/views.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/pages/main/index.vue b/cool-admin-vue/src/modules/base/pages/main/index.vue new file mode 100644 index 0000000..e078958 --- /dev/null +++ b/cool-admin-vue/src/modules/base/pages/main/index.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/static/css/index.scss b/cool-admin-vue/src/modules/base/static/css/index.scss new file mode 100644 index 0000000..e31abc7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/css/index.scss @@ -0,0 +1,81 @@ +#app { + height: 100vh; + width: 100vw; + overflow: hidden; +} + +:root { + --bg-color: var(--el-fill-color-lighter); +} + +a { + text-decoration: none; +} + +input, +button { + outline: none; +} + +input { + &:-webkit-autofill { + box-shadow: 0 0 0px 1000px white inset; + } +} + +// scrollbar +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar:horizontal { + height: 6px; +} + +::-webkit-scrollbar-track { + border-radius: 10px; +} + +::-webkit-scrollbar-thumb { + background-color: #0003; + border-radius: 10px; + transition: all 0.2s ease-in-out; +} + +::-webkit-scrollbar-thumb:hover { + cursor: pointer; + background-color: #0000004d; +} + +.dark ::-webkit-scrollbar-thumb { + background-color: #fff3; +} + +.dark ::-webkit-scrollbar-thumb:hover { + background-color: #fff6; +} + +// custom +.cl-comm__icon { + display: flex; + align-items: center; + justify-content: center; + height: 26px; + width: 26px; + background-color: var(--el-bg-color); + border: 1px solid var(--el-fill-color-dark); + border-radius: 6px; + transition: all 0.2s ease-in-out; + outline: none; + cursor: pointer; + flex-shrink: 0; + + .cl-svg { + font-size: 16px; + color: var(--el-text-color-primary); + } + + &:hover { + background-color: var(--el-fill-color-light); + } +} diff --git a/cool-admin-vue/src/modules/base/static/svg/amount.svg b/cool-admin-vue/src/modules/base/static/svg/amount.svg new file mode 100644 index 0000000..78fae7f --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/amount.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/back.svg b/cool-admin-vue/src/modules/base/static/svg/back.svg new file mode 100644 index 0000000..1212879 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/back.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/close-border.svg b/cool-admin-vue/src/modules/base/static/svg/close-border.svg new file mode 100644 index 0000000..4c125fa --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/close-border.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/close.svg b/cool-admin-vue/src/modules/base/static/svg/close.svg new file mode 100644 index 0000000..b45fab1 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/close.svg @@ -0,0 +1,21 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/delete.svg b/cool-admin-vue/src/modules/base/static/svg/delete.svg new file mode 100644 index 0000000..fccd1e8 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/edit.svg b/cool-admin-vue/src/modules/base/static/svg/edit.svg new file mode 100644 index 0000000..e1de5c5 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/edit.svg @@ -0,0 +1,21 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/exit.svg b/cool-admin-vue/src/modules/base/static/svg/exit.svg new file mode 100644 index 0000000..f6071c5 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/exit.svg @@ -0,0 +1,25 @@ + + + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/expand.svg b/cool-admin-vue/src/modules/base/static/svg/expand.svg new file mode 100644 index 0000000..453a7b3 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/export.svg b/cool-admin-vue/src/modules/base/static/svg/export.svg new file mode 100644 index 0000000..65c8e16 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/export.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/fail.svg b/cool-admin-vue/src/modules/base/static/svg/fail.svg new file mode 100644 index 0000000..545abc6 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/fail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/fold.svg b/cool-admin-vue/src/modules/base/static/svg/fold.svg new file mode 100644 index 0000000..feb1d41 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/fold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/github.svg b/cool-admin-vue/src/modules/base/static/svg/github.svg new file mode 100644 index 0000000..a2663ad --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/github.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/home.svg b/cool-admin-vue/src/modules/base/static/svg/home.svg new file mode 100644 index 0000000..d6f732d --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/home.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-activity.svg b/cool-admin-vue/src/modules/base/static/svg/icon-activity.svg new file mode 100644 index 0000000..be00ee9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-activity.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-amount.svg b/cool-admin-vue/src/modules/base/static/svg/icon-amount.svg new file mode 100644 index 0000000..13c68d9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-amount.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-app.svg b/cool-admin-vue/src/modules/base/static/svg/icon-app.svg new file mode 100644 index 0000000..1d82a1c --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-app.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-approve.svg b/cool-admin-vue/src/modules/base/static/svg/icon-approve.svg new file mode 100644 index 0000000..e77593c --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-approve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-auth.svg b/cool-admin-vue/src/modules/base/static/svg/icon-auth.svg new file mode 100644 index 0000000..b299f59 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-auth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-ban.svg b/cool-admin-vue/src/modules/base/static/svg/icon-ban.svg new file mode 100644 index 0000000..0376a77 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-ban.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-call.svg b/cool-admin-vue/src/modules/base/static/svg/icon-call.svg new file mode 100644 index 0000000..d35cabc --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-call.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-camera.svg b/cool-admin-vue/src/modules/base/static/svg/icon-camera.svg new file mode 100644 index 0000000..97dff8f --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-card.svg b/cool-admin-vue/src/modules/base/static/svg/icon-card.svg new file mode 100644 index 0000000..f6ae0e8 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-cart.svg b/cool-admin-vue/src/modules/base/static/svg/icon-cart.svg new file mode 100644 index 0000000..f6833eb --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-cart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-common.svg b/cool-admin-vue/src/modules/base/static/svg/icon-common.svg new file mode 100644 index 0000000..7fe6060 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-common.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-component.svg b/cool-admin-vue/src/modules/base/static/svg/icon-component.svg new file mode 100644 index 0000000..a44dae7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-component.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-count.svg b/cool-admin-vue/src/modules/base/static/svg/icon-count.svg new file mode 100644 index 0000000..daf26ba --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-count.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-crown.svg b/cool-admin-vue/src/modules/base/static/svg/icon-crown.svg new file mode 100644 index 0000000..71429de --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-crown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-data.svg b/cool-admin-vue/src/modules/base/static/svg/icon-data.svg new file mode 100644 index 0000000..1dbfe70 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-data.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-db.svg b/cool-admin-vue/src/modules/base/static/svg/icon-db.svg new file mode 100644 index 0000000..4a5d345 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-db.svg @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-delete.svg b/cool-admin-vue/src/modules/base/static/svg/icon-delete.svg new file mode 100644 index 0000000..610d33e --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-dept.svg b/cool-admin-vue/src/modules/base/static/svg/icon-dept.svg new file mode 100644 index 0000000..f0af65d --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-dept.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-design.svg b/cool-admin-vue/src/modules/base/static/svg/icon-design.svg new file mode 100644 index 0000000..3aed42f --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-design.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-device.svg b/cool-admin-vue/src/modules/base/static/svg/icon-device.svg new file mode 100644 index 0000000..f82b640 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-device.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-dict.svg b/cool-admin-vue/src/modules/base/static/svg/icon-dict.svg new file mode 100644 index 0000000..444ceee --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-discover.svg b/cool-admin-vue/src/modules/base/static/svg/icon-discover.svg new file mode 100644 index 0000000..3747d7e --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-discover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-doc.svg b/cool-admin-vue/src/modules/base/static/svg/icon-doc.svg new file mode 100644 index 0000000..d82fa34 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-doc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-download.svg b/cool-admin-vue/src/modules/base/static/svg/icon-download.svg new file mode 100644 index 0000000..b86f044 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-emoji.svg b/cool-admin-vue/src/modules/base/static/svg/icon-emoji.svg new file mode 100644 index 0000000..2809858 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-emoji.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-favor.svg b/cool-admin-vue/src/modules/base/static/svg/icon-favor.svg new file mode 100644 index 0000000..98ad58e --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-favor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-file.svg b/cool-admin-vue/src/modules/base/static/svg/icon-file.svg new file mode 100644 index 0000000..5ab59d6 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-folder.svg b/cool-admin-vue/src/modules/base/static/svg/icon-folder.svg new file mode 100644 index 0000000..7ad8ab7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-goods.svg b/cool-admin-vue/src/modules/base/static/svg/icon-goods.svg new file mode 100644 index 0000000..391d0ea --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-goods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-home.svg b/cool-admin-vue/src/modules/base/static/svg/icon-home.svg new file mode 100644 index 0000000..94cf445 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-hot.svg b/cool-admin-vue/src/modules/base/static/svg/icon-hot.svg new file mode 100644 index 0000000..b907eeb --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-hot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-info.svg b/cool-admin-vue/src/modules/base/static/svg/icon-info.svg new file mode 100644 index 0000000..4805c9a --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-iot.svg b/cool-admin-vue/src/modules/base/static/svg/icon-iot.svg new file mode 100644 index 0000000..f7ccae0 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-iot.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-light.svg b/cool-admin-vue/src/modules/base/static/svg/icon-light.svg new file mode 100644 index 0000000..b07a126 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-like.svg b/cool-admin-vue/src/modules/base/static/svg/icon-like.svg new file mode 100644 index 0000000..c49af81 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-like.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-list.svg b/cool-admin-vue/src/modules/base/static/svg/icon-list.svg new file mode 100644 index 0000000..a53f8ba --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-list.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-local.svg b/cool-admin-vue/src/modules/base/static/svg/icon-local.svg new file mode 100644 index 0000000..6bedcdf --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-local.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-log.svg b/cool-admin-vue/src/modules/base/static/svg/icon-log.svg new file mode 100644 index 0000000..e35c47b --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-map.svg b/cool-admin-vue/src/modules/base/static/svg/icon-map.svg new file mode 100644 index 0000000..9befbd4 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-match.svg b/cool-admin-vue/src/modules/base/static/svg/icon-match.svg new file mode 100644 index 0000000..f7c68ef --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-match.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-menu.svg b/cool-admin-vue/src/modules/base/static/svg/icon-menu.svg new file mode 100644 index 0000000..b58dbf9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-monitor.svg b/cool-admin-vue/src/modules/base/static/svg/icon-monitor.svg new file mode 100644 index 0000000..5fa7b77 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-monitor.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-msg.svg b/cool-admin-vue/src/modules/base/static/svg/icon-msg.svg new file mode 100644 index 0000000..54c0992 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-msg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-news.svg b/cool-admin-vue/src/modules/base/static/svg/icon-news.svg new file mode 100644 index 0000000..5cec609 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-news.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-notice.svg b/cool-admin-vue/src/modules/base/static/svg/icon-notice.svg new file mode 100644 index 0000000..5c8048d --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-notice.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-params.svg b/cool-admin-vue/src/modules/base/static/svg/icon-params.svg new file mode 100644 index 0000000..9683e72 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-params.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-phone.svg b/cool-admin-vue/src/modules/base/static/svg/icon-phone.svg new file mode 100644 index 0000000..8910aab --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-phone.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-pic.svg b/cool-admin-vue/src/modules/base/static/svg/icon-pic.svg new file mode 100644 index 0000000..02265cd --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-pic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-question.svg b/cool-admin-vue/src/modules/base/static/svg/icon-question.svg new file mode 100644 index 0000000..1731198 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-quick.svg b/cool-admin-vue/src/modules/base/static/svg/icon-quick.svg new file mode 100644 index 0000000..61bdb79 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-quick.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-rank.svg b/cool-admin-vue/src/modules/base/static/svg/icon-rank.svg new file mode 100644 index 0000000..60916c5 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-rank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-reward.svg b/cool-admin-vue/src/modules/base/static/svg/icon-reward.svg new file mode 100644 index 0000000..f01f848 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-reward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-search.svg b/cool-admin-vue/src/modules/base/static/svg/icon-search.svg new file mode 100644 index 0000000..166aebf --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-set.svg b/cool-admin-vue/src/modules/base/static/svg/icon-set.svg new file mode 100644 index 0000000..fb53057 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-set.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-tag.svg b/cool-admin-vue/src/modules/base/static/svg/icon-tag.svg new file mode 100644 index 0000000..84adbce --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-tag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-task.svg b/cool-admin-vue/src/modules/base/static/svg/icon-task.svg new file mode 100644 index 0000000..470280f --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-time.svg b/cool-admin-vue/src/modules/base/static/svg/icon-time.svg new file mode 100644 index 0000000..7ac6296 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-tutorial.svg b/cool-admin-vue/src/modules/base/static/svg/icon-tutorial.svg new file mode 100644 index 0000000..cc2323e --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-tutorial.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-unlock.svg b/cool-admin-vue/src/modules/base/static/svg/icon-unlock.svg new file mode 100644 index 0000000..78b6413 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-unlock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-user.svg b/cool-admin-vue/src/modules/base/static/svg/icon-user.svg new file mode 100644 index 0000000..66d8df7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-video.svg b/cool-admin-vue/src/modules/base/static/svg/icon-video.svg new file mode 100644 index 0000000..1eee5b9 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-vip.svg b/cool-admin-vue/src/modules/base/static/svg/icon-vip.svg new file mode 100644 index 0000000..31f4f67 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-vip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-warn.svg b/cool-admin-vue/src/modules/base/static/svg/icon-warn.svg new file mode 100644 index 0000000..3eab18d --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-warn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-work.svg b/cool-admin-vue/src/modules/base/static/svg/icon-work.svg new file mode 100644 index 0000000..bea8473 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-work.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/icon-workbench.svg b/cool-admin-vue/src/modules/base/static/svg/icon-workbench.svg new file mode 100644 index 0000000..ff959d3 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/icon-workbench.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/image.svg b/cool-admin-vue/src/modules/base/static/svg/image.svg new file mode 100644 index 0000000..fca2c66 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/image.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/import.svg b/cool-admin-vue/src/modules/base/static/svg/import.svg new file mode 100644 index 0000000..ae272f2 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/import.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/left.svg b/cool-admin-vue/src/modules/base/static/svg/left.svg new file mode 100644 index 0000000..f49a79b --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/my.svg b/cool-admin-vue/src/modules/base/static/svg/my.svg new file mode 100644 index 0000000..a422ae4 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/my.svg @@ -0,0 +1,21 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/order.svg b/cool-admin-vue/src/modules/base/static/svg/order.svg new file mode 100644 index 0000000..1dbbc68 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/order.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/play.svg b/cool-admin-vue/src/modules/base/static/svg/play.svg new file mode 100644 index 0000000..e5b5f40 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/plus-border.svg b/cool-admin-vue/src/modules/base/static/svg/plus-border.svg new file mode 100644 index 0000000..a36cf9d --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/plus-border.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/plus.svg b/cool-admin-vue/src/modules/base/static/svg/plus.svg new file mode 100644 index 0000000..4207733 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/refresh.svg b/cool-admin-vue/src/modules/base/static/svg/refresh.svg new file mode 100644 index 0000000..a9f1281 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/refresh.svg @@ -0,0 +1,21 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/right.svg b/cool-admin-vue/src/modules/base/static/svg/right.svg new file mode 100644 index 0000000..d488008 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/screen-full.svg b/cool-admin-vue/src/modules/base/static/svg/screen-full.svg new file mode 100644 index 0000000..a0e4c57 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/screen-full.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/screen-normal.svg b/cool-admin-vue/src/modules/base/static/svg/screen-normal.svg new file mode 100644 index 0000000..c5d80d5 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/screen-normal.svg @@ -0,0 +1,17 @@ + + + + diff --git a/cool-admin-vue/src/modules/base/static/svg/search.svg b/cool-admin-vue/src/modules/base/static/svg/search.svg new file mode 100644 index 0000000..54e1135 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/set.svg b/cool-admin-vue/src/modules/base/static/svg/set.svg new file mode 100644 index 0000000..aa84b0a --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/set.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/sort.svg b/cool-admin-vue/src/modules/base/static/svg/sort.svg new file mode 100644 index 0000000..f842b4b --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/sort.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/stats.svg b/cool-admin-vue/src/modules/base/static/svg/stats.svg new file mode 100644 index 0000000..25eec42 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/stats.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/success.svg b/cool-admin-vue/src/modules/base/static/svg/success.svg new file mode 100644 index 0000000..66d15fd --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/team.svg b/cool-admin-vue/src/modules/base/static/svg/team.svg new file mode 100644 index 0000000..32acf07 --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/team.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/static/svg/trend.svg b/cool-admin-vue/src/modules/base/static/svg/trend.svg new file mode 100644 index 0000000..aa8215b --- /dev/null +++ b/cool-admin-vue/src/modules/base/static/svg/trend.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cool-admin-vue/src/modules/base/store/app.ts b/cool-admin-vue/src/modules/base/store/app.ts new file mode 100644 index 0000000..0a0bd57 --- /dev/null +++ b/cool-admin-vue/src/modules/base/store/app.ts @@ -0,0 +1,69 @@ +import { defineStore } from 'pinia'; +import { reactive, ref } from 'vue'; +import { merge } from 'lodash-es'; +import { useBrowser } from '/@/cool'; +import { storage } from '/@/cool/utils'; +import { config } from '/@/config'; + +export const useAppStore = defineStore('app', function () { + const { browser, onScreenChange } = useBrowser(); + + // 基本信息 + const info = reactive({ + ...config.app + }); + + // 设置基本信息 + function set(data: any) { + merge(info, data); + storage.set('__app__', info); + } + + // 是否折叠 + const isFold = ref(false); + + // 折叠 + function fold(v?: boolean) { + if (v === undefined) { + v = !isFold.value; + } + + isFold.value = v; + } + + // 是否全屏 + const isFull = ref(false); + + // 设置全屏 + function setFull(state: boolean) { + isFull.value = state; + } + + // 事件 + const events = reactive<{ [key: string]: any[] }>({ + hasToken: [] + }); + + // 添加事件 + function addEvent(name: string, func: any) { + if (func) { + events[name].push(func); + } + } + + // 监听屏幕变化 + onScreenChange(() => { + isFold.value = browser.isMini; + }); + + return { + info, + isFold, + fold, + isFull, + setFull, + events, + set, + addEvent + }; +}); diff --git a/cool-admin-vue/src/modules/base/store/index.ts b/cool-admin-vue/src/modules/base/store/index.ts new file mode 100644 index 0000000..1aa03f8 --- /dev/null +++ b/cool-admin-vue/src/modules/base/store/index.ts @@ -0,0 +1,18 @@ +import { useAppStore } from './app'; +import { useMenuStore } from './menu'; +import { useProcessStore } from './process'; +import { useUserStore } from './user'; + +export function useStore() { + const app = useAppStore(); + const menu = useMenuStore(); + const process = useProcessStore(); + const user = useUserStore(); + + return { + app, + menu, + process, + user + }; +} diff --git a/cool-admin-vue/src/modules/base/store/menu.ts b/cool-admin-vue/src/modules/base/store/menu.ts new file mode 100644 index 0000000..5dfbc1d --- /dev/null +++ b/cool-admin-vue/src/modules/base/store/menu.ts @@ -0,0 +1,205 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { deepTree, revDeepTree, storage } from '/@/cool/utils'; +import { isArray, isEmpty, orderBy } from 'lodash-es'; +import { router, service } from '/@/cool'; +import { revisePath } from '../utils'; +import { config } from '/@/config'; + +// 本地缓存 +const data = storage.info(); + +export const useMenuStore = defineStore('menu', function () { + // 所有菜单 + const all = ref([]); + + // 视图路由 + const routes = ref([]); + + // 菜单组 + const group = ref(data['base.menuGroup'] || []); + + // 左侧菜单列表 + const list = ref([]); + + // 权限列表 + const perms = ref(data['base.menuPerms'] || []); + + // 设置左侧菜单 + function setMenu(i: number = 0) { + // 显示分组显示菜单 + if (config.app.menu.isGroup) { + list.value = group.value.filter(e => e.isShow)[i]?.children || []; + } else { + list.value = group.value; + } + } + + // 设置权限 + function setPerms(list: Menu.List) { + function deep(d: any) { + if (typeof d == 'object') { + if (d.permission) { + if (d.namespace) { + d._permission = {}; + for (const i in d.permission) { + d._permission[i] = + list.findIndex(e => + e + .replace(/:/g, '/') + .includes(`${d.namespace.replace('admin/', '')}/${i}`) + ) >= 0; + } + } else { + console.error('namespace is required', d); + } + } else { + for (const i in d) { + deep(d[i]); + } + } + } + } + + perms.value = list; + storage.set('base.menuPerms', list); + + deep(service); + } + + // 设置视图 + function setRoutes(list: Menu.List) { + // 获取第一个菜单路径 + const fp = getPath(group.value); + + // 查找符合路由 + const route = list.find(e => (e.meta!.isHome = e.path == fp)); + + // 过滤菜单 + routes.value = list.filter(e => e.type == 1); + + if (route) { + // 移除旧路由 + router.del('home'); + router.del('homeRedirect'); + + // 添加一个重定向 + if (route.path != '/') { + const item = routes.value.find(e => e.name == 'homeRedirect'); + + if (item) { + item.path = route.path; + } else { + routes.value.push({ + path: route.path, + redirect: '/', + name: 'homeRedirect' + } as any); + } + } + + // 设置为首页 + route.path = '/'; + route.name = 'home'; + } + } + + // 设置菜单组 + function setGroup(list: Menu.List) { + group.value = orderBy(deepTree(list), 'orderNum'); + storage.set('base.menuGroup', group.value); + } + + // 获取菜单,权限信息 + async function get() { + function next(res: { menus: Menu.List; perms?: any[] }) { + // 所有菜单 + all.value = res.menus; + + // 菜单格式化 + const list = res.menus + ?.filter(e => e.type != 2) + .map(e => { + const path = revisePath(e.router || String(e.id)); + const isShow = e.isShow === undefined ? true : e.isShow; + + return { + ...e, + path, + isShow, + meta: { + ...e.meta, + label: e.name, // 菜单名称的唯一标识 + keepAlive: e.keepAlive || 0 + }, + name: `${e.name}-${e.id}`, // 避免重复命名之前的冲突 + children: [] + }; + }); + + // 设置权限 + setPerms(res.perms || []); + + // 设置菜单组 + setGroup(list); + + // 设置视图路由 + setRoutes(list); + + // 设置菜单 + setMenu(); + + return list; + } + + // 自定义菜单 + if (!isEmpty(config.app.menu.list)) { + next({ + menus: revDeepTree(config.app.menu.list || []) + }); + } else { + // 动态菜单 + await service.base.comm.permmenu().then(next); + } + } + + // 获取菜单路径 + function getPath(data: Menu.Item | Menu.List) { + const list = isArray(data) ? data : [data]; + + let path = ''; + + function deep(arr: Menu.List) { + arr.forEach((e: Menu.Item) => { + switch (e.type) { + case 0: + deep(e.children || []); + break; + case 1: + if (!path) { + path = e.path; + } + break; + } + }); + } + + deep(list); + + return path; + } + + return { + all, + routes, + group, + list, + perms, + get, + setPerms, + setMenu, + setRoutes, + setGroup, + getPath + }; +}); diff --git a/cool-admin-vue/src/modules/base/store/process.ts b/cool-admin-vue/src/modules/base/store/process.ts new file mode 100644 index 0000000..076a1c0 --- /dev/null +++ b/cool-admin-vue/src/modules/base/store/process.ts @@ -0,0 +1,74 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { assign } from 'lodash-es'; + +export const useProcessStore = defineStore('process', function () { + const list = ref([]); + + // 添加 + function add(data: any) { + list.value.forEach((e: Process.Item) => { + e.active = false; + }); + + if (!data.meta) { + data.meta = {}; + } + + if (!data.meta?.isHome && data.meta?.process !== false) { + const index = list.value.findIndex(e => e.path === data.path); + + if (index < 0) { + list.value.push({ + ...data, + active: true + }); + } else { + assign(list.value[index], data, { active: true }); + } + } + } + + // 关闭当前 + function close() { + const index = list.value.findIndex(e => e.active); + + if (index > -1) { + list.value.splice(index, 1); + } + } + + // 移除 + function remove(index: number) { + list.value.splice(index, 1); + } + + // 设置 + function set(data: Process.Item[]) { + list.value = data; + } + + // 清空 + function clear() { + list.value = []; + } + + // 设置标题 + function setTitle(title: string) { + const item = list.value.find(e => e.active); + + if (item) { + item.meta.label = title; + } + } + + return { + list, + add, + remove, + close, + set, + clear, + setTitle + }; +}); diff --git a/cool-admin-vue/src/modules/base/store/user.ts b/cool-admin-vue/src/modules/base/store/user.ts new file mode 100644 index 0000000..071a2cf --- /dev/null +++ b/cool-admin-vue/src/modules/base/store/user.ts @@ -0,0 +1,88 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { storage } from '/@/cool/utils'; +import { service, router } from '/@/cool'; + +// 本地缓存 +const data = storage.info(); + +export const useUserStore = defineStore('user', function () { + // 标识 + const token = ref(data.token); + + // 设置标识 + function setToken(data: { + token: string; + expire: number; + refreshToken: string; + refreshExpire: number; + }) { + // 请求的唯一标识 + token.value = data.token; + storage.set('token', data.token, data.expire); + + // 刷新 token 的唯一标识 + storage.set('refreshToken', data.refreshToken, data.refreshExpire); + } + + // 刷新标识 + async function refreshToken(): Promise { + return new Promise((resolve, reject) => { + service.base.open + .refreshToken({ + refreshToken: storage.get('refreshToken') + }) + .then(res => { + setToken(res); + resolve(res.token); + }) + .catch(err => { + logout(); + reject(err); + }); + }); + } + + // 用户信息 + const info = ref(data.userInfo); + + // 设置用户信息 + function set(value: any) { + info.value = value; + storage.set('userInfo', value); + } + + // 清除用户 + function clear() { + storage.remove('userInfo'); + storage.remove('token'); + token.value = ''; + info.value = null; + } + + // 退出 + async function logout() { + clear(); + router.clear(); + router.push('/login'); + } + + // 获取用户信息 + async function get() { + return service.base.comm.person().then(res => { + set(res); + return res; + }); + } + + return { + token, + info, + get, + set, + logout, + clear, + setToken, + refreshToken + }; +}); diff --git a/cool-admin-vue/src/modules/base/types/index.d.ts b/cool-admin-vue/src/modules/base/types/index.d.ts new file mode 100644 index 0000000..81675fe --- /dev/null +++ b/cool-admin-vue/src/modules/base/types/index.d.ts @@ -0,0 +1,52 @@ +declare namespace Menu { + enum Type { + '目录' = 0, + '菜单' = 1, + '权限' = 2 + } + + interface Item { + id: number; + parentId: number; + path: string; + router?: string; + viewPath?: string; + type: Type; + name: string; + icon: string; + badge?: number; + badgeColor?: 'primary' | 'success' | 'warning' | 'danger' | 'info'; + orderNum: number; + isShow: number | boolean; + keepAlive?: number; + meta?: { + label?: string; + keepAlive?: number | boolean; + iframeUrl?: string; + isHome?: boolean; + [key: string]: any; + }; + children?: Item[]; + component?: any; + redirect?: string; + [key: string]: any; + } + + type List = Item[]; +} + +declare namespace Process { + interface Item { + active: boolean; + name: string; + path: string; + fullPath: string; + meta: { + label: string; + [key: string]: any; + }; + [key: string]: any; + } + + type List = Item[]; +} diff --git a/cool-admin-vue/src/modules/base/utils/index.ts b/cool-admin-vue/src/modules/base/utils/index.ts new file mode 100644 index 0000000..fa24a0b --- /dev/null +++ b/cool-admin-vue/src/modules/base/utils/index.ts @@ -0,0 +1,9 @@ +export function revisePath(path: string) { + if (!path) { + return ''; + } + + return path.startsWith('/') ? path : `/${path}`; +} + +export * from './permission'; diff --git a/cool-admin-vue/src/modules/base/utils/permission.ts b/cool-admin-vue/src/modules/base/utils/permission.ts new file mode 100644 index 0000000..efa9f60 --- /dev/null +++ b/cool-admin-vue/src/modules/base/utils/permission.ts @@ -0,0 +1,30 @@ +import { useStore } from '../store'; +import { isObject } from 'lodash-es'; + +function parse(value: any) { + const { menu } = useStore(); + + if (typeof value == 'string') { + return value ? menu.perms.some((e: any) => e.includes(value.replace(/\s/g, ''))) : false; + } else { + return Boolean(value); + } +} + +export function checkPerm(value: string | { or?: string[]; and?: string[] }) { + if (!value) { + return false; + } + + if (isObject(value)) { + if (value.or) { + return value.or.some(parse); + } + + if (value.and) { + return value.and.some((e: any) => !parse(e)) ? false : true; + } + } + + return parse(value); +} diff --git a/cool-admin-vue/src/modules/base/views/frame.vue b/cool-admin-vue/src/modules/base/views/frame.vue new file mode 100644 index 0000000..4c0a491 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/frame.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/views/info.vue b/cool-admin-vue/src/modules/base/views/info.vue new file mode 100644 index 0000000..8c836e7 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/info.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/views/log.vue b/cool-admin-vue/src/modules/base/views/log.vue new file mode 100644 index 0000000..3204876 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/log.vue @@ -0,0 +1,153 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/menu/components/exp.vue b/cool-admin-vue/src/modules/base/views/menu/components/exp.vue new file mode 100644 index 0000000..eb85384 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/menu/components/exp.vue @@ -0,0 +1,109 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/menu/components/imp.vue b/cool-admin-vue/src/modules/base/views/menu/components/imp.vue new file mode 100644 index 0000000..73faf95 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/menu/components/imp.vue @@ -0,0 +1,122 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/menu/index.vue b/cool-admin-vue/src/modules/base/views/menu/index.vue new file mode 100644 index 0000000..597a004 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/menu/index.vue @@ -0,0 +1,404 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/param.vue b/cool-admin-vue/src/modules/base/views/param.vue new file mode 100644 index 0000000..c7ef57e --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/param.vue @@ -0,0 +1,226 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/role.vue b/cool-admin-vue/src/modules/base/views/role.vue new file mode 100644 index 0000000..8dadea2 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/role.vue @@ -0,0 +1,165 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/user/components/dept-list.vue b/cool-admin-vue/src/modules/base/views/user/components/dept-list.vue new file mode 100644 index 0000000..19d5519 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/user/components/dept-list.vue @@ -0,0 +1,503 @@ + + + + + diff --git a/cool-admin-vue/src/modules/base/views/user/components/user-move.vue b/cool-admin-vue/src/modules/base/views/user/components/user-move.vue new file mode 100644 index 0000000..8d51495 --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/user/components/user-move.vue @@ -0,0 +1,74 @@ + + + diff --git a/cool-admin-vue/src/modules/base/views/user/index.vue b/cool-admin-vue/src/modules/base/views/user/index.vue new file mode 100644 index 0000000..bcd761c --- /dev/null +++ b/cool-admin-vue/src/modules/base/views/user/index.vue @@ -0,0 +1,342 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/config.ts b/cool-admin-vue/src/modules/demo/config.ts new file mode 100644 index 0000000..13c4911 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/config.ts @@ -0,0 +1,27 @@ +import { type ModuleConfig } from '/@/cool'; + +export default (): ModuleConfig => { + return { + components: [() => import('./views/crud/components/code.vue')], + + views: [ + { + // 单个参数 + // path: "/demo/test/route/:id", + + // 多个参数 + // path: "/demo/test/route/:id/:name", + + // 参数可选 + path: '/demo/test/route/:id/:name?', + + // 更多看文档:https://router.vuejs.org/zh/guide/essentials/route-matching-syntax.html + + meta: { + label: '动态路由参数' + }, + component: () => import('./views/test/route.vue') + } + ] + }; +}; diff --git a/cool-admin-vue/src/modules/demo/directives/color.ts b/cool-admin-vue/src/modules/demo/directives/color.ts new file mode 100644 index 0000000..8c5b1c6 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/directives/color.ts @@ -0,0 +1,5 @@ +export default { + created(el: HTMLElement, binding: any) { + el.style.color = binding.value; + } +}; diff --git a/cool-admin-vue/src/modules/demo/locales/en.json b/cool-admin-vue/src/modules/demo/locales/en.json new file mode 100644 index 0000000..20572fb --- /dev/null +++ b/cool-admin-vue/src/modules/demo/locales/en.json @@ -0,0 +1,54 @@ +{ + "浏览量": "Views", + "访客数": "Visitors", + "总用户数": "Total Users", + "日增用户数": "Daily New Users", + "付款笔数": "Payment Transactions", + "转化率": "Conversion Rate", + "总销售额": "Total Sales", + "周同比": "Week-on-Week", + "日同比": "Day-on-Day", + "类别占比": "Category Share", + "启用": "Enabled", + "描述": "Description", + "未知": "Unknown", + "男": "Male", + "女": "Female", + "禁用": "Disabled", + "正常": "Normal", + "已注销": "Canceled", + "小程序": "Mini Program", + "公众号": "Official Account", + "H5": "H5", + "登录唯一ID": "Login Unique ID", + "头像": "Avatar", + "昵称": "Nickname", + "手机号": "Mobile Phone Number", + "性别": "Gender", + "状态": "Status", + "登录方式": "Login Method", + "来源": "Source", + "密码": "Password", + "介绍": "Introduction", + "余额": "Balance", + "标题": "Title", + "ID": "ID", + "创建时间": "Create Time", + "更新时间": "Update Time", + "搜索手机号、昵称": "Search Mobile Number, Nickname", + "{year}年": "{year} Year", + "销售金额": "Sales Amount", + "销售订单": "Sales Order", + "{i}月": "{i} Month", + "热门商品排行": "Popular Product Ranking", + "今日": "Today", + "本周": "This Week", + "本月": "This Month", + "全年": "Full Year", + "排名": "Ranking", + "商品名称": "Product Name", + "商品金额": "Product Amount", + "下单次数": "Order Placed Times", + "日涨幅": "Daily Increase", + "上架时间": "Listing Time" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/demo/locales/zh-cn.json b/cool-admin-vue/src/modules/demo/locales/zh-cn.json new file mode 100644 index 0000000..8182c78 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/locales/zh-cn.json @@ -0,0 +1,54 @@ +{ + "未知": "未知", + "男": "男", + "女": "女", + "禁用": "禁用", + "正常": "正常", + "已注销": "已注销", + "小程序": "小程序", + "公众号": "公众号", + "H5": "H5", + "登录唯一ID": "登录唯一ID", + "头像": "头像", + "昵称": "昵称", + "手机号": "手机号", + "性别": "性别", + "状态": "状态", + "登录方式": "登录方式", + "来源": "来源", + "密码": "密码", + "介绍": "介绍", + "余额": "余额", + "标题": "标题", + "ID": "ID", + "创建时间": "创建时间", + "更新时间": "更新时间", + "搜索手机号、昵称": "搜索手机号、昵称", + "{year}年": "{year}年", + "销售金额": "销售金额", + "销售订单": "销售订单", + "{i}月": "{i}月", + "热门商品排行": "热门商品排行", + "今日": "今日", + "本周": "本周", + "本月": "本月", + "全年": "全年", + "排名": "排名", + "商品名称": "商品名称", + "商品金额": "商品金额", + "下单次数": "下单次数", + "日涨幅": "日涨幅", + "上架时间": "上架时间", + "浏览量": "浏览量", + "访客数": "访客数", + "总用户数": "总用户数", + "日增用户数": "日增用户数", + "付款笔数": "付款笔数", + "转化率": "转化率", + "总销售额": "总销售额", + "周同比": "周同比", + "日同比": "日同比", + "类别占比": "类别占比", + "启用": "启用", + "描述": "描述" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/demo/locales/zh-tw.json b/cool-admin-vue/src/modules/demo/locales/zh-tw.json new file mode 100644 index 0000000..74117a5 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/locales/zh-tw.json @@ -0,0 +1,54 @@ +{ + "浏览量": "瀏覽量", + "访客数": "訪客數", + "总用户数": "總用戶數", + "日增用户数": "日增用戶數", + "付款笔数": "付款筆數", + "转化率": "轉化率", + "总销售额": "總銷售額", + "周同比": "周同比", + "日同比": "日同比", + "类别占比": "類別占比", + "启用": "啟用", + "描述": "描述", + "未知": "未知", + "男": "男", + "女": "女", + "禁用": "禁用", + "正常": "正常", + "已注销": "已註銷", + "小程序": "小程序", + "公众号": "公眾號", + "H5": "H5", + "登录唯一ID": "登錄唯一ID", + "头像": "頭像", + "昵称": "暱稱", + "手机号": "手機號", + "性别": "性別", + "状态": "狀態", + "登录方式": "登錄方式", + "来源": "來源", + "密码": "密碼", + "介绍": "介紹", + "余额": "餘額", + "标题": "標題", + "ID": "ID", + "创建时间": "創建時間", + "更新时间": "更新時間", + "搜索手机号、昵称": "搜索手機號、暱稱", + "{year}年": "{year}年", + "销售金额": "銷售金額", + "销售订单": "銷售訂單", + "{i}月": "{i}月", + "热门商品排行": "熱門商品排行", + "今日": "今日", + "本周": "本周", + "本月": "本月", + "全年": "全年", + "排名": "排名", + "商品名称": "商品名稱", + "商品金额": "商品金額", + "下单次数": "下單次數", + "日涨幅": "日漲幅", + "上架时间": "上架時間" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/base.vue new file mode 100644 index 0000000..0b74d4a --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/base.vue @@ -0,0 +1,138 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/custom.vue b/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/custom.vue new file mode 100644 index 0000000..94ce40c --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/adv-search/custom.vue @@ -0,0 +1,152 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/code.vue b/cool-admin-vue/src/modules/demo/views/crud/components/code.vue new file mode 100644 index 0000000..96b90f1 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/code.vue @@ -0,0 +1,45 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/all.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/all.vue new file mode 100644 index 0000000..bea553f --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/all.vue @@ -0,0 +1,578 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/base.vue new file mode 100644 index 0000000..b3851af --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/base.vue @@ -0,0 +1,145 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/dict.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/dict.vue new file mode 100644 index 0000000..da64ac8 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/dict.vue @@ -0,0 +1,164 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/event.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/event.vue new file mode 100644 index 0000000..f75dd27 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/event.vue @@ -0,0 +1,182 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/select-table.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/select-table.vue new file mode 100644 index 0000000..0a4a33c --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/select-table.vue @@ -0,0 +1,155 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/service.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/service.vue new file mode 100644 index 0000000..fda349b --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/service.vue @@ -0,0 +1,179 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/crud/user-select.vue b/cool-admin-vue/src/modules/demo/views/crud/components/crud/user-select.vue new file mode 100644 index 0000000..afbd6ab --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/crud/user-select.vue @@ -0,0 +1,77 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/children.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/children.vue new file mode 100644 index 0000000..3730ef4 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/children.vue @@ -0,0 +1,118 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/component/index.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/index.vue new file mode 100644 index 0000000..c404408 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/index.vue @@ -0,0 +1,124 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-labels.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-labels.vue new file mode 100644 index 0000000..e21e5af --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-labels.vue @@ -0,0 +1,42 @@ + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-status.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-status.vue new file mode 100644 index 0000000..2dfabea --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-status.vue @@ -0,0 +1,47 @@ + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work.vue new file mode 100644 index 0000000..6ff625f --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work.vue @@ -0,0 +1,63 @@ + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work2.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work2.vue new file mode 100644 index 0000000..818a215 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/component/select-work2.vue @@ -0,0 +1,42 @@ + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/config.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/config.vue new file mode 100644 index 0000000..32d6604 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/config.vue @@ -0,0 +1,122 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/crud.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/crud.vue new file mode 100644 index 0000000..ca4d892 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/crud.vue @@ -0,0 +1,151 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/disabled.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/disabled.vue new file mode 100644 index 0000000..1a109ad --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/disabled.vue @@ -0,0 +1,64 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/event.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/event.vue new file mode 100644 index 0000000..201ecf8 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/event.vue @@ -0,0 +1,93 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/group.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/group.vue new file mode 100644 index 0000000..fc2e07b --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/group.vue @@ -0,0 +1,105 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/hidden.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/hidden.vue new file mode 100644 index 0000000..e28a665 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/hidden.vue @@ -0,0 +1,77 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/layout.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/layout.vue new file mode 100644 index 0000000..b4ab074 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/layout.vue @@ -0,0 +1,98 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/open.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/open.vue new file mode 100644 index 0000000..8105135 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/open.vue @@ -0,0 +1,84 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/options.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/options.vue new file mode 100644 index 0000000..8c51305 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/options.vue @@ -0,0 +1,172 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/index.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/index.vue new file mode 100644 index 0000000..bd65b40 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/index.vue @@ -0,0 +1,109 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/role.ts b/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/role.ts new file mode 100644 index 0000000..f924c1e --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/plugin/role.ts @@ -0,0 +1,20 @@ +/** + * 角色权限控制 + * @param role + * @returns + */ +export function setRole(role?: string): ClForm.Plugin { + return ({ exposed }) => { + function deep(arr: ClForm.Item[]) { + arr.forEach(e => { + if (e.role) { + e.hidden = e.role != role; + } + + deep(e.children || []); + }); + } + + deep(exposed.config.items); + }; +} diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/required.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/required.vue new file mode 100644 index 0000000..7de191f --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/required.vue @@ -0,0 +1,94 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/rules.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/rules.vue new file mode 100644 index 0000000..818f8d5 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/rules.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/form/setFocus.vue b/cool-admin-vue/src/modules/demo/views/crud/components/form/setFocus.vue new file mode 100644 index 0000000..467e039 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/form/setFocus.vue @@ -0,0 +1,63 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/other/context-menu.vue b/cool-admin-vue/src/modules/demo/views/crud/components/other/context-menu.vue new file mode 100644 index 0000000..3551d16 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/other/context-menu.vue @@ -0,0 +1,71 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/other/tips.vue b/cool-admin-vue/src/modules/demo/views/crud/components/other/tips.vue new file mode 100644 index 0000000..15d545d --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/other/tips.vue @@ -0,0 +1,150 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.scss b/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.scss new file mode 100644 index 0000000..bd99427 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.scss @@ -0,0 +1,28 @@ +.tsx-list { + .item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid var(--el-border-color); + padding: 10px; + margin-bottom: 10px; + cursor: pointer; + border-radius: var(--el-border-radius-base); + + .el-icon { + display: none; + } + + &:hover { + background-color: var(--el-bg-color-page); + } + + &.is-active { + color: var(--el-color-primary); + + .el-icon { + display: block; + } + } + } +} diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.tsx b/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.tsx new file mode 100644 index 0000000..ae3396b --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/other/tsx/index.tsx @@ -0,0 +1,109 @@ +import { defineComponent, ref } from 'vue'; +import { Check } from '@element-plus/icons-vue'; +import './index.scss'; + +interface Item { + name: string; + value: number; +} + +export default defineComponent({ + emits: ['checked'], + + setup(props, { emit, expose, slots }) { + // 列表数据 + const list = ref([ + { + name: '鸡腿堡', + value: 1 + }, + { + name: '牛肉堡', + value: 2 + } + ]); + + // 选择值 + const active = ref(); + + // 是否可见 + const visible = ref(false); + + // 打开 + function open() { + visible.value = true; + } + + // 选择 + function toCheck(item: Item) { + active.value = item.value; + + // 自定义事件 + emit('checked', item); + } + + // 暴露方法和变量,使上级可以使用 ref 的方式来调用 + expose({ + toCheck + }); + + // 必须返回一个方法 + return () => { + return ( +
+
+ + tsx + + tsx示例 +
+ +
+ 预览 + + + {/* ref 的绑定值必须 .value */} + +
+ {/* 循环的使用 */} + {list.value.map(item => { + // 插槽的使用 + return slots.default ? ( + slots.default(item) + ) : ( +
toCheck(item)} + > + {item.name} + + + + +
+ ); + })} +
+
+
+ +
+ 2024-01-01 +
+
+ ); + }; + } + + // 不推荐用该方法,在 setup 中返回模板信息 + // render() { + // return
; + // } +}); diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/search/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/search/base.vue new file mode 100644 index 0000000..ef9c311 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/search/base.vue @@ -0,0 +1,147 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/search/collapse.vue b/cool-admin-vue/src/modules/demo/views/crud/components/search/collapse.vue new file mode 100644 index 0000000..afcb4ad --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/search/collapse.vue @@ -0,0 +1,110 @@ + + + +x diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/search/custom.vue b/cool-admin-vue/src/modules/demo/views/crud/components/search/custom.vue new file mode 100644 index 0000000..51e8bf7 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/search/custom.vue @@ -0,0 +1,176 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/search/layout.vue b/cool-admin-vue/src/modules/demo/views/crud/components/search/layout.vue new file mode 100644 index 0000000..ae4d3ee --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/search/layout.vue @@ -0,0 +1,149 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/search/plugin.vue b/cool-admin-vue/src/modules/demo/views/crud/components/search/plugin.vue new file mode 100644 index 0000000..93ba5a3 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/search/plugin.vue @@ -0,0 +1,128 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/base.vue new file mode 100644 index 0000000..053a3cd --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/base.vue @@ -0,0 +1,109 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/children.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/children.vue new file mode 100644 index 0000000..fd4b71f --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/children.vue @@ -0,0 +1,95 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/column-custom.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/column-custom.vue new file mode 100644 index 0000000..c362331 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/column-custom.vue @@ -0,0 +1,108 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/component/index.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/component/index.vue new file mode 100644 index 0000000..005d153 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/component/index.vue @@ -0,0 +1,108 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/component/user-info.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/component/user-info.vue new file mode 100644 index 0000000..f4fd232 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/component/user-info.vue @@ -0,0 +1,34 @@ + + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/context-menu.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/context-menu.vue new file mode 100644 index 0000000..43f77f4 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/context-menu.vue @@ -0,0 +1,191 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/dict.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/dict.vue new file mode 100644 index 0000000..083297d --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/dict.vue @@ -0,0 +1,156 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/formatter.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/formatter.vue new file mode 100644 index 0000000..3cd0172 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/formatter.vue @@ -0,0 +1,95 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/hidden.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/hidden.vue new file mode 100644 index 0000000..c81ad16 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/hidden.vue @@ -0,0 +1,127 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/op.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/op.vue new file mode 100644 index 0000000..d15cbe0 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/op.vue @@ -0,0 +1,182 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/base.vue new file mode 100644 index 0000000..d8f2445 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/base.vue @@ -0,0 +1,130 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/row-edit.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/row-edit.vue new file mode 100644 index 0000000..de8c2d0 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/row-edit.vue @@ -0,0 +1,124 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/to-tree.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/to-tree.vue new file mode 100644 index 0000000..d2c58be --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/plugin/to-tree.vue @@ -0,0 +1,87 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/search.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/search.vue new file mode 100644 index 0000000..88cb8ea --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/search.vue @@ -0,0 +1,137 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/selection.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/selection.vue new file mode 100644 index 0000000..87549ac --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/selection.vue @@ -0,0 +1,109 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/slot.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/slot.vue new file mode 100644 index 0000000..929ddb1 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/slot.vue @@ -0,0 +1,97 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/span-method.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/span-method.vue new file mode 100644 index 0000000..d752ed9 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/span-method.vue @@ -0,0 +1,115 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/table/summary.vue b/cool-admin-vue/src/modules/demo/views/crud/components/table/summary.vue new file mode 100644 index 0000000..05e37cf --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/table/summary.vue @@ -0,0 +1,95 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/upsert/base.vue b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/base.vue new file mode 100644 index 0000000..96f76d0 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/base.vue @@ -0,0 +1,133 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/upsert/event.vue b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/event.vue new file mode 100644 index 0000000..e7b15a9 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/event.vue @@ -0,0 +1,210 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/index.vue b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/index.vue new file mode 100644 index 0000000..f5dfcc0 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/index.vue @@ -0,0 +1,200 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts new file mode 100644 index 0000000..472a9c8 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts @@ -0,0 +1,14 @@ +import { registerFormHook } from '@cool-vue/crud'; + +// 注册 hook +registerFormHook('pca2', (value, { method, form, prop }) => { + if (method == 'bind') { + return [form.province, form.city, form.district]; + } else { + const [province, city, district] = value || []; + form.province = province; + form.city = city; + form.district = district; + form[prop] = undefined; + } +}); diff --git a/cool-admin-vue/src/modules/demo/views/crud/components/upsert/mode.vue b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/mode.vue new file mode 100644 index 0000000..1b7fb8d --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/components/upsert/mode.vue @@ -0,0 +1,146 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/crud/index.vue b/cool-admin-vue/src/modules/demo/views/crud/index.vue new file mode 100644 index 0000000..a7a712d --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/crud/index.vue @@ -0,0 +1,327 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/category-ratio.vue b/cool-admin-vue/src/modules/demo/views/home/components/category-ratio.vue new file mode 100644 index 0000000..d8f6159 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/category-ratio.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/count-effect.vue b/cool-admin-vue/src/modules/demo/views/home/components/count-effect.vue new file mode 100644 index 0000000..845b446 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/count-effect.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/count-paid.vue b/cool-admin-vue/src/modules/demo/views/home/components/count-paid.vue new file mode 100644 index 0000000..c33d996 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/count-paid.vue @@ -0,0 +1,30 @@ + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/count-user.vue b/cool-admin-vue/src/modules/demo/views/home/components/count-user.vue new file mode 100644 index 0000000..aee2948 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/count-user.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/count-views.vue b/cool-admin-vue/src/modules/demo/views/home/components/count-views.vue new file mode 100644 index 0000000..5e591c8 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/count-views.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/hot-goods.vue b/cool-admin-vue/src/modules/demo/views/home/components/hot-goods.vue new file mode 100644 index 0000000..8c02f23 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/hot-goods.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/components/tab-chart.vue b/cool-admin-vue/src/modules/demo/views/home/components/tab-chart.vue new file mode 100644 index 0000000..0656b78 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/components/tab-chart.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/home/index.vue b/cool-admin-vue/src/modules/demo/views/home/index.vue new file mode 100644 index 0000000..34ea22a --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/home/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/cool-admin-vue/src/modules/demo/views/test/route.vue b/cool-admin-vue/src/modules/demo/views/test/route.vue new file mode 100644 index 0000000..9f438f1 --- /dev/null +++ b/cool-admin-vue/src/modules/demo/views/test/route.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/cool-admin-vue/src/modules/dict/config.ts b/cool-admin-vue/src/modules/dict/config.ts new file mode 100644 index 0000000..291820c --- /dev/null +++ b/cool-admin-vue/src/modules/dict/config.ts @@ -0,0 +1,16 @@ +import { type ModuleConfig } from '/@/cool'; +import { useDict } from './index'; + +export default (): ModuleConfig => { + return { + ignore: { + NProgress: ['/dict/info/data'] + }, + onLoad({ hasToken }) { + const { dict } = useDict(); + hasToken(() => { + dict.refresh(); + }); + } + }; +}; diff --git a/cool-admin-vue/src/modules/dict/index.ts b/cool-admin-vue/src/modules/dict/index.ts new file mode 100644 index 0000000..26e543e --- /dev/null +++ b/cool-admin-vue/src/modules/dict/index.ts @@ -0,0 +1,7 @@ +import { useStore } from './store'; + +export function useDict() { + return { + ...useStore() + }; +} diff --git a/cool-admin-vue/src/modules/dict/locales/en.json b/cool-admin-vue/src/modules/dict/locales/en.json new file mode 100644 index 0000000..60e2043 --- /dev/null +++ b/cool-admin-vue/src/modules/dict/locales/en.json @@ -0,0 +1,16 @@ +{ + "搜索名称": "Search Name", + "请填写值": "Please fill in the value", + "使用文件": "Use File", + "类型": "Type", + "字典列表": "Dictionary List", + "名称": "Name", + "上级节点": "Parent Node", + "值": "Value", + "排序": "Sort", + "备注": "Remarks", + "新增": "Add", + "ID": "ID", + "创建时间": "Create Time", + "更新时间": "Update Time" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/dict/locales/zh-cn.json b/cool-admin-vue/src/modules/dict/locales/zh-cn.json new file mode 100644 index 0000000..196194c --- /dev/null +++ b/cool-admin-vue/src/modules/dict/locales/zh-cn.json @@ -0,0 +1,16 @@ +{ + "搜索名称": "搜索名称", + "请填写值": "请填写值", + "使用文件": "使用文件", + "类型": "类型", + "字典列表": "字典列表", + "名称": "名称", + "上级节点": "上级节点", + "值": "值", + "排序": "排序", + "备注": "备注", + "新增": "新增", + "ID": "ID", + "创建时间": "创建时间", + "更新时间": "更新时间" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/dict/locales/zh-tw.json b/cool-admin-vue/src/modules/dict/locales/zh-tw.json new file mode 100644 index 0000000..f91fb7a --- /dev/null +++ b/cool-admin-vue/src/modules/dict/locales/zh-tw.json @@ -0,0 +1,16 @@ +{ + "搜索名称": "搜尋名稱", + "请填写值": "請填寫值", + "使用文件": "使用文件", + "类型": "類型", + "字典列表": "字典列表", + "名称": "名稱", + "上级节点": "上級節點", + "值": "值", + "排序": "排序", + "备注": "備註", + "新增": "新增", + "ID": "ID", + "创建时间": "創建時間", + "更新时间": "更新時間" +} \ No newline at end of file diff --git a/cool-admin-vue/src/modules/dict/store/dict.ts b/cool-admin-vue/src/modules/dict/store/dict.ts new file mode 100644 index 0000000..f4c3638 --- /dev/null +++ b/cool-admin-vue/src/modules/dict/store/dict.ts @@ -0,0 +1,65 @@ +import { defineStore } from 'pinia'; +import { computed, reactive, toRaw } from 'vue'; +import { service } from '/@/cool'; +import { deepTree } from '/@/cool/utils'; +import { isDev } from '/@/config'; +import { assign, isArray, orderBy } from 'lodash-es'; +import { deepFind, isEmpty } from '../utils'; + +const useDictStore = defineStore('dict', () => { + // 对象数据 + const data = reactive({}); + + // 获取 + function get(name: Dict.Key, sort?: 'desc' | 'asc') { + return computed(() => orderBy(data[name] || [], 'orderNum', sort)); + } + + // 查找 + function find(name: Dict.Key, value: any | any[]) { + const arr = isArray(value) ? value : [value]; + return arr.filter(e => e !== undefined).map(v => deepFind(v, get(name).value)); + } + + // 刷新 + async function refresh(types?: Dict.Key[]) { + return service.dict.info + .data({ + types: types?.filter(e => !isEmpty(e)) + }) + .then((res: Dict.Data) => { + const d = {}; + + for (const [i, arr] of Object.entries(res)) { + arr.forEach(e => { + e.label = e.name; + + if (isEmpty(e.value)) { + e.value = e.id; + } + }); + + d[i] = deepTree(arr, 'desc'); + } + + assign(data, d); + + if (isDev) { + console.group('字典数据'); + console.log(toRaw(data)); + console.groupEnd(); + } + + return data; + }); + } + + return { + data, + get, + find, + refresh + }; +}); + +export { useDictStore }; diff --git a/cool-admin-vue/src/modules/dict/store/index.ts b/cool-admin-vue/src/modules/dict/store/index.ts new file mode 100644 index 0000000..9261de5 --- /dev/null +++ b/cool-admin-vue/src/modules/dict/store/index.ts @@ -0,0 +1,9 @@ +import { useDictStore } from './dict'; + +export function useStore() { + const dict = useDictStore(); + + return { + dict + }; +} diff --git a/cool-admin-vue/src/modules/dict/types/index.d.ts b/cool-admin-vue/src/modules/dict/types/index.d.ts new file mode 100644 index 0000000..d2cd99d --- /dev/null +++ b/cool-admin-vue/src/modules/dict/types/index.d.ts @@ -0,0 +1,15 @@ +namespace Dict { + type Key = Eps.DictKey | (string & {}); + + interface Item { + id: string; + label: string; + value: any; + children?: Item[]; + [key: string]: any; + } + + interface Data { + [key: string]: Item[]; + } +} diff --git a/cool-admin-vue/src/modules/dict/utils/index.ts b/cool-admin-vue/src/modules/dict/utils/index.ts new file mode 100644 index 0000000..cf19f63 --- /dev/null +++ b/cool-admin-vue/src/modules/dict/utils/index.ts @@ -0,0 +1,31 @@ +export function deepFind(value: any, list: any[], options?: { allLevels: boolean }) { + const { allLevels = true } = options || {}; + + function deep(arr: any[], name: string[]): any | undefined { + for (const e of arr) { + if (e.value === value) { + if (allLevels) { + return { + ...e, + label: [...name, e.label].join(' / ') + }; + } else { + return e; + } + } else if (e.children) { + const d = deep(e.children, [...name, e.label]); + + if (d !== undefined) { + return d; + } + } + } + return undefined; + } + + return deep(list, []); +} + +export function isEmpty(val: any) { + return val === '' || val === null || val === undefined; +} diff --git a/cool-admin-vue/src/modules/dict/views/list.vue b/cool-admin-vue/src/modules/dict/views/list.vue new file mode 100644 index 0000000..6d2859b --- /dev/null +++ b/cool-admin-vue/src/modules/dict/views/list.vue @@ -0,0 +1,281 @@ + + + diff --git a/cool-admin-vue/src/modules/helper/components/ai-code/btn.vue b/cool-admin-vue/src/modules/helper/components/ai-code/btn.vue new file mode 100644 index 0000000..b40449c --- /dev/null +++ b/cool-admin-vue/src/modules/helper/components/ai-code/btn.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/cool-admin-vue/src/modules/helper/components/ai-code/dev.vue b/cool-admin-vue/src/modules/helper/components/ai-code/dev.vue new file mode 100644 index 0000000..6aaf325 --- /dev/null +++ b/cool-admin-vue/src/modules/helper/components/ai-code/dev.vue @@ -0,0 +1,76 @@ +