diff --git a/code/link/link-backend/.gitignore b/code/link/link-backend/.gitignore deleted file mode 100644 index 5ff6309b..00000000 --- a/code/link/link-backend/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/code/link/link-backend/.idea/.gitignore b/code/link/link-backend/.idea/.gitignore index 13566b81..73f69e09 100644 --- a/code/link/link-backend/.idea/.gitignore +++ b/code/link/link-backend/.idea/.gitignore @@ -1,8 +1,8 @@ # Default ignored files /shelf/ /workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ # Datasource local storage ignored files /dataSources/ /dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/code/link/link-backend/.idea/compiler.xml b/code/link/link-backend/.idea/compiler.xml new file mode 100644 index 00000000..37f12852 --- /dev/null +++ b/code/link/link-backend/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/encodings.xml b/code/link/link-backend/.idea/encodings.xml index aa00ffab..63e90019 100644 --- a/code/link/link-backend/.idea/encodings.xml +++ b/code/link/link-backend/.idea/encodings.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/code/link/link-backend/.idea/jarRepositories.xml b/code/link/link-backend/.idea/jarRepositories.xml new file mode 100644 index 00000000..841afb50 --- /dev/null +++ b/code/link/link-backend/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/misc.xml b/code/link/link-backend/.idea/misc.xml index 132404bc..d5cd6143 100644 --- a/code/link/link-backend/.idea/misc.xml +++ b/code/link/link-backend/.idea/misc.xml @@ -8,7 +8,5 @@ - - - + \ No newline at end of file diff --git a/code/link/link-backend/.idea/runConfigurations.xml b/code/link/link-backend/.idea/runConfigurations.xml new file mode 100644 index 00000000..797acea5 --- /dev/null +++ b/code/link/link-backend/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/vcs.xml b/code/link/link-backend/.idea/vcs.xml index c2365ab1..ba43f699 100644 --- a/code/link/link-backend/.idea/vcs.xml +++ b/code/link/link-backend/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/code/link/link-backend/pom.xml b/code/link/link-backend/pom.xml index 019a253e..676dcab2 100644 --- a/code/link/link-backend/pom.xml +++ b/code/link/link-backend/pom.xml @@ -1,206 +1,115 @@ - + 4.0.0 - - cn.g3soft + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + com.example link-backend - 1.0-SNAPSHOT + 0.0.1-SNAPSHOT + backend + backend 1.8 - UTF-8 - UTF-8 - 3.1.1 - 2.5.15 - 1.2.23 - 3.10.3 - 1.3.1 - 3.12.0 - - - 9.0.96 - 1.2.13 - 5.3.39 + 4.1.86.Final - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - org.springframework - spring-framework-bom - ${spring-framework.version} - pom - import - - - - - - com.squareup.okhttp3 - okhttp - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-aop - - - - - org.projectlombok - lombok - - - - - org.springframework.boot - spring-boot-starter-validation - - - - - ch.qos.logback - logback-core - ${logback.version} - - - - ch.qos.logback - logback-classic - ${logback.version} - - - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - - org.apache.tomcat.embed - tomcat-embed-el - ${tomcat.version} - - - - org.apache.tomcat.embed - tomcat-embed-websocket - ${tomcat.version} - - - - - org.yaml - snakeyaml - - - - - com.alibaba - druid-spring-boot-starter - ${druid.version} - - - - - com.auth0 - java-jwt - ${jwt.version} - - - - net.sourceforge.jtds - jtds - ${jtds.version} - - org.springframework.boot spring-boot-starter-jdbc - - org.apache.commons - commons-lang3 - ${commons.version} + com.alibaba + druid + 1.2.18 + + + com.microsoft.sqlserver + mssql-jdbc + 6.2.2.jre8 + + + io.jsonwebtoken + jjwt + 0.9.1 + + + com.fasterxml.jackson.core + jackson-databind + + + + p6spy + p6spy + 3.9.1 + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot + spring-boot-devtools + true + + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + junit + junit + + io.netty netty-all - 4.1.112.Final + ${netty.version} + - - jar - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - false - - - - org.springframework.boot spring-boot-maven-plugin - 2.5.15 - - true - - - - - repackage - - - - \ No newline at end of file + + + + + aliyun + https://maven.aliyun.com/repository/public + + true + + + true + + + + + + + aliyun + https://maven.aliyun.com/repository/public + + + + diff --git a/code/link/link-backend/src/main/java/com/link/LinkApplication.java b/code/link/link-backend/src/main/java/com/link/LinkApplication.java index ca9637eb..19884ded 100644 --- a/code/link/link-backend/src/main/java/com/link/LinkApplication.java +++ b/code/link/link-backend/src/main/java/com/link/LinkApplication.java @@ -1,16 +1,11 @@ package com.link; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -@SpringBootApplication(exclude = { - DataSourceAutoConfiguration.class, - DruidDataSourceAutoConfigure.class , -}) +@SpringBootApplication public class LinkApplication { public static void main(String[] args) { - SpringApplication.run(LinkApplication.class,args); + SpringApplication.run(LinkApplication.class); } } diff --git a/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java deleted file mode 100644 index 8dc340e4..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.link.api.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -@Configuration -public class WebCorsConfig { - @Bean - public CorsFilter corsFilter() { - // 1.创建 CORS 配置对象 - CorsConfiguration config = new CorsConfiguration(); - // 支持域 - config.addAllowedOriginPattern("*"); - // 是否发送 Cookie - config.setAllowCredentials(true); - // 支持请求方式 - config.addAllowedMethod("*"); - // 允许的原始请求头部信息 - config.addAllowedHeader("*"); - // 暴露的头部信息 - config.addExposedHeader("*"); - // 有效期 1800秒,设置预检请求(OPTIONS请求)的缓存时间,1800秒意味着客户端可以缓存这个设置1800秒 - config.setMaxAge(1800L); - // 2.添加地址映射 - UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); - corsConfigurationSource.registerCorsConfiguration("/**", config); - // 3.返回 CorsFilter 对象 - return new CorsFilter(corsConfigurationSource); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java deleted file mode 100644 index 0c6b2096..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.link.api.config.datasource; - -import lombok.*; - -import java.util.Map; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class DataSourceConfig { - private Map datasource; - - @Setter - @Getter - public static class DbConfig { - // getters & setters - private String url; - private String username; - private String password; - private String driverClassName; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java deleted file mode 100644 index 449f4dee..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.link.api.config.datasource; - -import com.link.api.context.DataSourceContextHolder; -import com.alibaba.druid.filter.Filter; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.wall.WallConfig; -import com.alibaba.druid.wall.WallFilter; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; -import org.springframework.stereotype.Component; -import org.yaml.snakeyaml.Yaml; - -import java.io.InputStream; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.*; - -@Component -public class DataSourceManager extends AbstractRoutingDataSource { - - private static final String EXTERNAL_CONFIG_PATH = "file:config/datasource.yml"; - private static final String CLASSPATH_CONFIG_PATH = "classpath:datasource.yml"; - - @javax.annotation.Resource - private ResourceLoader resourceLoader; - - private final Map targetDataSourceMap = new HashMap<>(); - - public DataSourceManager() { - super.setTargetDataSources(targetDataSourceMap); - } - - @Override - protected Object determineCurrentLookupKey() { - return DataSourceContextHolder.getDataSourceKey(); - } - - public void loadDatasource() { - try { - // 从配置文件中解析出数据源配置对象 - DataSourceConfig config = resolveConfigResource(); - Map newDsMap = config.getDatasource(); - Set newKeys = newDsMap.keySet(); - - // 关闭并移除已经被删除(配置文件中不再存在)的旧数据源 - Set existingKeys = new HashSet<>(targetDataSourceMap.keySet()); - for (Object key : existingKeys) { - // 如果旧数据源中有但新配置中没有,说明该数据源被删除了 - if (!newKeys.contains((String) key)) { - // 移除旧数据源并关闭连接池资源 - DruidDataSource ds = (DruidDataSource) targetDataSourceMap.remove(key); - ds.close(); - System.out.println("移除并关闭旧数据源 [" + key + "]"); - } - } - - // 遍历新配置,进行新增或更新处理 - for (Map.Entry entry : newDsMap.entrySet()) { - String key = entry.getKey(); - DataSourceConfig.DbConfig newConfig = entry.getValue(); - - // 获取已有的数据源实例(如果存在) - DruidDataSource existingDS = (DruidDataSource) targetDataSourceMap.get(key); - - // 如果已有数据源配置和新配置相同,则跳过初始化 - if (existingDS != null && isSameConfig(existingDS, newConfig)) { - System.out.println("数据源 [" + key + "] 配置未变更,跳过初始化"); - continue; - } - - // 如果已有数据源但配置不同,关闭旧数据源 - if (existingDS != null) { - existingDS.close(); - System.out.println("关闭已变更数据源 [" + key + "]"); - } - - try { - // 预先验证驱动和连接是否可用,防止错误配置 - Class.forName(newConfig.getDriverClassName()); - DriverManager.getConnection(newConfig.getUrl(), newConfig.getUsername(), newConfig.getPassword()).close(); - - // 创建并初始化新的 DruidDataSource 实例 -// DruidDataSource newDS = buildDruidDataSource(newConfig); - // 创建并初始化新的 HikariDataSource 实例 - HikariDataSource newDS = buildHikariDataSource(newConfig); - - // 放入数据源映射中,替换旧数据源(或新增) - targetDataSourceMap.put(key, newDS); - System.out.println("数据源 [" + key + "] 初始化成功"); - - } catch (Exception e) { - // 初始化失败时输出错误日志,不影响其他数据源加载 - System.err.printf("数据源 [%s] 初始化失败:%s%n", key, e.getMessage()); - } - } - - // 通知父类更新数据源路由配置 - super.setTargetDataSources(targetDataSourceMap); - super.afterPropertiesSet(); - - } catch (Exception e) { - // 捕获整个过程中的异常,打印错误日志 - System.err.println("加载数据源配置异常:" + e.getMessage()); - } - } - - private boolean isSameConfig(DruidDataSource oldDS, DataSourceConfig.DbConfig newConfig) { - return Objects.equals(oldDS.getUrl(), newConfig.getUrl()) && - Objects.equals(oldDS.getUsername(), newConfig.getUsername()) && - Objects.equals(oldDS.getPassword(), newConfig.getPassword()) && - Objects.equals(oldDS.getDriverClassName(), newConfig.getDriverClassName()); - } - - private DataSourceConfig resolveConfigResource() throws Exception { - Resource resource = resourceLoader.getResource(EXTERNAL_CONFIG_PATH); - if (!resource.exists()) { - resource = resourceLoader.getResource(CLASSPATH_CONFIG_PATH); - } - if (!resource.exists()) { - throw new Exception("配置文件未找到:" + EXTERNAL_CONFIG_PATH + " 或 " + CLASSPATH_CONFIG_PATH); - } - - try (InputStream in = resource.getInputStream()) { - return new Yaml().loadAs(in, DataSourceConfig.class); - } - } - - private HikariDataSource buildHikariDataSource(DataSourceConfig.DbConfig config){ - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setDriverClassName(config.getDriverClassName()); - hikariConfig.setJdbcUrl(config.getUrl()); - hikariConfig.setUsername(config.getUsername()); - hikariConfig.setPassword(config.getPassword()); - hikariConfig.setConnectionTestQuery("SELECT 1"); // SQL Server 可执行语句 - - return new HikariDataSource(hikariConfig); - } - - private DruidDataSource buildDruidDataSource(DataSourceConfig.DbConfig config) throws Exception { - DruidDataSource ds = getDefaultDruidDataSource(); - - ds.setDriverClassName(config.getDriverClassName()); - ds.setUrl(config.getUrl()); - ds.setUsername(config.getUsername()); - ds.setPassword(config.getPassword()); - - ds.init(); - return ds; - } - - private DruidDataSource getDefaultDruidDataSource() throws SQLException { - DruidDataSource dataSource = new DruidDataSource(); - // 启动程序时,在连接池中初始化多少个连接(10-50已足够) - dataSource.setInitialSize(5); - // 回收空闲连接时,将保证至少有 minIdle 个连接(与 initialSize 相同) - dataSource.setMinIdle(5); - // 连接池中最多支持多少个活动会话 - dataSource.setMaxActive(100); - // 程序向连接池中请求连接时,超过 maxWait 的值后,认为本次请求失败,即连接池,没有可用连接,单位毫秒,设置 -1 时表示无限等待(建议值为100) - dataSource.setMaxWait(100); - /* - 缓存通过以下两个方法发起的 SQL: - public PreparedStatement prepareStatement(String sql) - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - (建议值为 true) - */ - dataSource.setPoolPreparedStatements(true); - // 每个连接最多缓存多少个 SQL(建议值为 20) - dataSource.setMaxPoolPreparedStatementPerConnectionSize(20); - // 检查空闲连接的频率,单位毫秒,非正整数时表示不进行检查(建议值:2000) - dataSource.setTimeBetweenEvictionRunsMillis(2000); - // 连接池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将回收该连接,要小于防火墙超时设置 net.netfilter.nf_conntrack_tcp_timeout_established 的设置 - dataSource.setMinEvictableIdleTimeMillis(600000); - // 配置一个连接在池中最大生存的时间,单位是毫秒 - dataSource.setMaxEvictableIdleTimeMillis(900000); - // 程序没有 close 连接且空闲时长超过 minEvictableIdleTimeMillis,则会执行 validationQuery 指定的 SQL,以保证该程序连接不会池 kill 掉,其范围不超过 minIdle 指定的连接个数(建议值为 true) - dataSource.setKeepAlive(true); - // 检查池中的连接是否仍可用的 SQL 语句,druid 会连接到数据库执行该 SQL,如果正常返回,则表示连接可用,否则表示连接不可用 - dataSource.setValidationQuery("SELECT 1"); - // 当程序请求连接,池在分配连接时,是否先检查该连接是否有效(高效,并且保证安全性;建议值为 true) - dataSource.setTestWhileIdle(true); - // 程序申请连接时,进行连接有效性检查(低效,影响性能;建议值为 false) - dataSource.setTestOnBorrow(false); - // 程序返还连接时,进行连接有效性检查(低效,影响性能;建议值为 false) - dataSource.setTestOnReturn(false); - // 物理连接初始化的时候执行的 sql -// Collection connectionInitSqls = new ArrayList<>(10); -// connectionInitSqls.add("SELECT 1 FROM DUAL"); -// dataSource.setConnectionInitSqls(connectionInitSqls); - /* - 这里配置的是插件,常用的插件有: - 监控统计:stat - 日志监控:log4j2 - 防御 SQL 注入:wall - */ - dataSource.setFilters("stat"); - // 是否合并多个 DruidDataSource 的监控数据 - dataSource.setUseGlobalDataSourceStat(true); - // 监控统计 - // 是否启用慢 SQL 记录 - dataSource.addConnectionProperty("druid.stat.logSlowSql", "true"); - // 执行时间超过 slowSqlMillis 的就是慢,单位毫秒(建议值 500) - dataSource.addConnectionProperty("druid.stat.slowSqlMillis", "500"); - // 要求程序从池中 get 到连接后,N 秒后必须 close,否则 druid 会强制回收该连接,不管该连接中是活动还是空闲,以防止进程不会进行 close 而霸占连接(建议值为 false,当发现程序有未正常 close 连接时设置为 true) -// dataSource.setRemoveAbandoned(false); - // 设置 druid 强制回收连接的时限,当程序从池中 get 到连接开始算起,超过此值后,druid将强制回收该连接,单位秒(应大于业务运行最长时间) -// dataSource.setRemoveAbandonedTimeout(); - // 当 druid 强制回收连接后,是否将 stack trace 记录到日志中(建议值为 true) -// dataSource.setLogAbandoned(true); - // 连接属性。比如设置一些连接池统计方面的配置 druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 -// dataSource.setConnectProperties(); - - // 防御 SQL 注入 - WallFilter wallFilter = new WallFilter(); - WallConfig config = new WallConfig(); - // 是否允许执行 DELETE 语句(建议值为 false) - config.setDeleteAllow(false); - // 是否允许删除表(建议值为 false) - config.setDropTableAllow(false); - wallFilter.setConfig(config); - - // 插件代理 - List proxyFilters = new ArrayList<>(10); - proxyFilters.add(wallFilter); - dataSource.setProxyFilters(proxyFilters); - return dataSource; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java deleted file mode 100644 index 316014aa..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.link.api.config.datasource; - -import com.alibaba.druid.support.http.StatViewServlet; -import com.alibaba.druid.support.http.WebStatFilter; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.*; - -@Configuration -public class DruidConfig { - - @Bean - public ServletRegistrationBean statViewServlet() { - // 监控信息显示页面 - StatViewServlet statViewServlet = new StatViewServlet(); - // 访问监控信息显示页面的 url 路径(建议值为 /druid/* ) - String urlPattern = "/druid/*"; - - ServletRegistrationBean bean = new ServletRegistrationBean<>(statViewServlet, urlPattern); - - Map initParams = new HashMap<>(); - // 是否允许清空统计数据 - initParams.put("resetEnable", "false"); - // 登录监控信息显示页面的用户名 - initParams.put("loginUsername", "admin"); - // 登录监控信息显示页面的密码 - initParams.put("loginPassword", "admin"); - // 允许访问控制(格式:ip地址、ip地址/子网掩码位数)逗号分隔多个地址 - initParams.put("allow", "127.0.0.1"); - // 拒绝访问控制(格式:ip地址、ip地址/子网掩码位数)逗号分隔多个地址 -// initParams.put("deny", ""); - - bean.setInitParameters(initParams); - return bean; - } - - @Bean - public FilterRegistrationBean webStatFilter() { - FilterRegistrationBean bean = new FilterRegistrationBean<>(); - // 网络监控过滤器(用于采集 web-jdbc 关联监控的数据) - bean.setFilter(new WebStatFilter()); - // 过滤所有的 url 路径 - Collection urlPatterns = new ArrayList<>(); - urlPatterns.add("/*"); - bean.setUrlPatterns(urlPatterns); - - Map initParams = new HashMap<>(); - // 排除不必要采集的 url 路径,以逗号“,”分割 - initParams.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,*.map,/druid/*"); - // 是否使用 session 监控功能 - initParams.put("sessionStatEnable", "true"); - // 是否使用 session 监控最大数量(默认是1000) - initParams.put("sessionStatMaxCount", "1000"); - // 使得 druid 能够知道当前的 session 的用户是谁,根据需要,把改值修改为你 user 信息保存在 session 中的 sessionName - initParams.put("principalSessionName", "session_user_key"); - // 如果你的 user 信息保存在 cookie 中,你可以配置 principalCookieName,使得 druid 知道当前的 user 是谁,根据需要,把该值修改为你 user 信息保存在 cookie 中的 cookieName - initParams.put("principalCookieName", "cookie_user_key"); - // 是否监控单个 url 调用的 sql 列表 - initParams.put("profileEnable", "true"); - bean.setInitParameters(initParams); - return bean; - } - -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java deleted file mode 100644 index ad92504d..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.link.api.config.datasource; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - - -@Slf4j -@Component -public class LoadDataSourceRunner implements CommandLineRunner { - @Resource - private DataSourceManager dataSourceManager; - - @Override - public void run(String... args) throws Exception { - dataSourceManager.loadDatasource(); - } -} - diff --git a/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java b/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java deleted file mode 100644 index 796884c0..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.link.api.constants; - -public class ResponseStatusConstants { - /** - * 操作成功 - */ - public static final Integer SUCCESS = 200; - - /** - * 系统内部错误 - */ - public static final Integer ERROR = 500; - - /** - * 缺少参数,参数格式有误,请求路径有误等 - */ - public static final Integer BAD_REQUEST = 400; - - /** - * 未找到资源,或者该资源已丢失 - */ - public static final Integer NOT_FOUND = 404; - - /** - * 登录失效或已过期 - */ - public static final Integer UNAUTHORIZED = 401; -} diff --git a/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java b/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java deleted file mode 100644 index 6169f6a6..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.link.api.context; - -public class DataSourceContextHolder { - private static final ThreadLocal contextHolder = new ThreadLocal<>(); - - public static void setDataSourceKey(String dataSourceKey) { - contextHolder.set(dataSourceKey); - } - - public static String getDataSourceKey() { - return contextHolder.get(); - } - - public static void clearDataSourceKey() { - contextHolder.remove(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java b/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java new file mode 100644 index 00000000..4842e6c5 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java @@ -0,0 +1,40 @@ +package com.link.api.controller; + + +import com.link.api.model.UserInfo; +import com.link.api.repository.UserRepository; +import com.link.api.util.JwtUtil; + +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@CrossOrigin +@RestController +public class AuthController { + @PostMapping("/api/login") + public Map login(@RequestBody Map body) { + Map response = new HashMap<>(); + String userId = body.get("userid"); + String password = body.get("password"); + String orgId = body.get("orgid"); + + if (userId == null || password == null || orgId == null) { + response.put("message", "Missing parameters"); + return response; + } + UserInfo userInfo = UserRepository.getUserPasswordById(orgId, userId); + if (userInfo != null && userInfo.getPassword().equals(password)) { + String token = JwtUtil.generateToken(userId, orgId); + response.put("token", token); + response.put("userInfo", userInfo); + response.put("message", "Login successful"); + } else { + response.put("message", "Invalid credentials"); + } + return response; + } + + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java new file mode 100644 index 00000000..f8520309 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java @@ -0,0 +1,59 @@ +package com.link.api.controller; + +import com.link.api.util.JsonUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +@RestController +@CrossOrigin +public class ColumnConfigController { + + /** + * 获取某个表的列配置,返回统一结构的 JSON + */ + @GetMapping("/api/columns") + public Result getColumnConfig(@RequestParam String tableName, HttpServletRequest request) { + + + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/columns:"+orgId); + List> data = JsonUtils.queryColumnConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + + + @GetMapping("/api/columnsEdit") + public Result getColumnEditConfig(@RequestParam String tableName, HttpServletRequest request) { + + try { + String orgId = UserContext.getOrgId(); + List> data = JsonUtils.queryColumnEditConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + + @GetMapping("/api/columnsSelect") + public Result ColumnsSelect(@RequestParam String tableName, HttpServletRequest request) { + + try { + String orgId = UserContext.getOrgId(); + List> data = JsonUtils.queryColumnsSelectConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java deleted file mode 100644 index 2418b6cb..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.link.api.controller; - - -import com.link.api.config.datasource.DataSourceManager; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -@RestController -@RequestMapping("/config") -public class ConfigController { - @Resource - private DataSourceManager dataSourceManager; - @GetMapping("/refreshDb") - public void refreshDb(){ - dataSourceManager.loadDatasource(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java new file mode 100644 index 00000000..a98c2160 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java @@ -0,0 +1,175 @@ +package com.link.api.controller; + +import com.link.api.util.PaginationUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +@RestController +@CrossOrigin +public class DataListController { + + @GetMapping("/api/pagtable") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam int page, + @RequestParam int size, + @RequestParam(required = false) String whereClause // 可选条件 + ) { + try { + String orgId = UserContext.getOrgId(); + System.out.println("api/pagtable:"+orgId); + + String json = PaginationUtils.paginateSqlServer2008( + orgId, tableName, pkColumn, page, size, whereClause + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/pagdetail") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "subid") String pkColumn, + @RequestParam(required = false) String whereClause // 可选条件 + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/pagdetail:"+orgId); + String json = PaginationUtils.detailSqlServer2008( + orgId, tableName, pkColumn, whereClause + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + + @GetMapping("/api/selectcompany") + public Result getCompanyData( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectcompany:"+orgId); + String json = PaginationUtils.getbasicdataSqlServer2008(orgId,tableName,query); + + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + + @GetMapping("/api/selectbasicbyfieldname") + public Result getPagedTableDataname( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String fieldName, + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectbasicbyfieldname:"+orgId); + String json = PaginationUtils.getbasicdatabyfieldnameSqlServer2008(orgId,tableName,fieldName,query); + + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectlistbysql") + public Result getDataBySql(@RequestParam String sql){ + try { + String orgId = UserContext.getOrgId(); + String json = PaginationUtils.getDataBySql(orgId,sql); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectbasicbyID") + public Result getPagedTableDataID( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String fieldName, + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectbasicbyID:"+orgId); + String json = PaginationUtils.getbasicdatabyidSqlServer2008(orgId,tableName,fieldName,query); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectbasicbyquery") + public Result getPagedTableDataSelectQuery( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String query, + @RequestParam String orderBy + + ) { + try { + String orgId = UserContext.getOrgId(); + String json = PaginationUtils.getbasicdatabyselectquerySqlServer2008(orgId,tableName,query,orderBy); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/pagtableSelect") + public Result getPagedTableSelectData( + @RequestParam String selectid, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam int page, + @RequestParam int size, + @RequestParam(required = false) String whereClause, // 可选条件 + @RequestParam String psparams + ) { + + String[] psparamsArr = psparams.split(","); // ✅ 转换为数组 + + try { + String orgId = UserContext.getOrgId(); + + String json = PaginationUtils.paginateSelectSqlServer2008( + orgId, selectid, pkColumn, page, size, whereClause,psparamsArr + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java new file mode 100644 index 00000000..f636632e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java @@ -0,0 +1,91 @@ +package com.link.api.controller; + +import com.link.api.util.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +import java.sql.SQLException; +import java.util.Map; + +@RestController +@CrossOrigin +public class DataObjController { + @GetMapping("/api/getobject") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam String pkColumnValue + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/pagtable:" + orgId); + String json = QueryByPkUtils.queryByPrimaryKey( + orgId, tableName, pkColumn, pkColumnValue + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/deleteobject") + public Result deleteobject( + @RequestParam String moduleid, + @RequestParam String ID + + ) throws SQLException { + //String orgCode = "G3HD"; + String recordId = ID; // 要删除的主键ID + String orgCode = UserContext.getOrgId(); + + Result> result = BatchTableDelete.executdeletetable(orgCode, moduleid, recordId); + + if (result.isSuccess()) { + System.out.println("删除成功!"); + result.getData().forEach((k, v) -> + System.out.println("表/统计项: " + k + " -> 删除条数: " + v) + ); + } else { + System.out.println("删除失败: " + result.getMessage()); + } + return result; + } + + + @GetMapping("/api/getDefaultvalue") + public Result getDefaultvalue( + @RequestParam String tableName + ) { + try { + String orgId = UserContext.getOrgId(); + // 获取JSON字符串 + String jsonResult = TableDataService.generateTableDataWithDefaults(orgId, tableName); + + // 将JSON字符串反序列化为Result对象 + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonResult, Result.class); + } catch (Exception e) { + return Result.fail("获取默认值异常:" + e.getMessage()); + } + } + + @GetMapping("/api/getMustfield") + public Result getMustFields( + @RequestParam String tableName + ) { + try { + String orgId = UserContext.getOrgId(); + // 获取JSON字符串 + String jsonResult = TableDataService.getMustFields(orgId, tableName); + + // 将JSON字符串反序列化为Result对象 + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonResult, Result.class); + } catch (Exception e) { + return Result.fail("获取默认值异常:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java new file mode 100644 index 00000000..8b9c2009 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java @@ -0,0 +1,112 @@ +package com.link.api.controller; + + +import com.link.api.util.*; +import com.link.api.util.Result; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping +@CrossOrigin +public class DataSaveController { + + @PostMapping("/api/saveobject") + public Result saveObject(@RequestBody Map request) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/saveobject:" + orgId); + + ObjectMapper mapper = new ObjectMapper(); + + String[] tableNames = mapper.convertValue(request.get("tableNames"), String[].class); + String[] keyFields = mapper.convertValue(request.get("keyFields"), String[].class); + + // 把对象数组转为字符串数组(再传给 batchChanges) + String[] inserts = convertToJsonArrayStrings(request.get("inserts")); + String[] updates = convertToJsonArrayStrings(request.get("updates")); + String[] deletes = convertToJsonArrayStrings(request.get("deletes")); + + return BatchTable.batchChanges(orgId, tableNames, keyFields, inserts, updates, deletes); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + @PostMapping("/api/saveobjectV2") + public Result saveObjectV2(@RequestBody List> requestDataList) { + try { + + String orgId = UserContext.getOrgId(); + // 遍历处理每个表的数据 + for (Map tableData : requestDataList) { + String tableName = (String) tableData.get("tableName"); + String keyField = (String) tableData.get("keyField"); + List> inserts = (List>) tableData.get("inserts"); + List> updates = (List>) tableData.get("updates"); + List> deletes = (List>) tableData.get("deletes"); + + // System.out.println("处理表: " + tableName); + // System.out.println("插入数据: " + inserts); + // System.out.println("updates: " + updates); + // System.out.println("deletes: " + deletes); + } + return BatchTableV2.executeDatabaseOperations(orgId, requestDataList); + // return Result.ok("处理成功"); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + @PostMapping("/api/saveobjectV3") + public Result saveobjectV3(@RequestBody List> requestDataList) { + try { + + String orgId = UserContext.getOrgId(); + + return BatchTableV3.executeDatabaseOperations(orgId, requestDataList); + // return Result.ok("处理成功"); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + + @PostMapping("/api/saveColumn") + public Result postSaveColumn(@RequestBody List> requestDataList) { + try { + String orgId = UserContext.getOrgId(); + + return BatchColumn.postSaveColumn(orgId, requestDataList); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + @PostMapping("/api/saveColumnEdit") + public Result postSaveColumnEdit(@RequestBody List> requestDataList) { + try { + String orgId = UserContext.getOrgId(); + + return BatchColumn.postSaveColumnEdit(orgId, requestDataList); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + private String[] convertToJsonArrayStrings(Object listObject) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + List list = mapper.convertValue(listObject, new TypeReference>() { + }); + String[] result = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + result[i] = mapper.writeValueAsString(list.get(i)); + } + return result; + } + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java b/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java new file mode 100644 index 00000000..156b36ad --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java @@ -0,0 +1,25 @@ +package com.link.api.controller; + +import com.link.api.util.JwtAuthenticationFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean jwtFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + + JwtAuthenticationFilter filter = new JwtAuthenticationFilter(); + registrationBean.setFilter(filter); + + registrationBean.addUrlPatterns("/*"); // 拦截所有请求 + registrationBean.setName("jwtAuthenticationFilter"); + registrationBean.setOrder(1); // 优先级,越小越靠前执行 + + return registrationBean; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java new file mode 100644 index 00000000..bcfc3564 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java @@ -0,0 +1,32 @@ +package com.link.api.controller; + +import com.link.api.util.*; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +@RestController +@CrossOrigin +public class SearchConfigController { + @GetMapping("/api/Searchconfig") + public Result getColumnConfig(@RequestParam String tableName, HttpServletRequest request) { + + + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/Searchconfig:"+orgId); + List> data = JsonUtils.querySearchConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java b/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java new file mode 100644 index 00000000..2a3920dc --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java @@ -0,0 +1,139 @@ +package com.link.api.controller; + +import com.link.api.model.Student; +import com.link.api.repository.StudentDaoJDBC; +import com.link.api.util.JwtPayload; +import com.link.api.util.JwtUtil; +import com.link.api.util.Result; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@CrossOrigin +@RestController +public class StudentController { + + @Autowired + private StudentDaoJDBC studentDAO; + + + private JwtPayload extractPayload(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + return null; + } + + String token = authHeader.substring(7); + return JwtUtil.parseToken(token); + } + + + + @GetMapping + public Result getAll(HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + try { + List data = studentDAO.findAll(); + if (data != null) { + return Result.ok("新增成功", data); + } else { + return Result.fail("新增失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + + } + + @PostMapping + public Result create(@RequestBody Student student, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + try { + int newId = studentDAO.insert(student); + if (newId > 0) { + return Result.ok("新增成功", newId); + } else { + return Result.fail("新增失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + + } + + @PutMapping("/{id}") + public Result update(@PathVariable Integer id, @RequestBody Student student, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + student.setB_id(id); + + try { + int newId = studentDAO.update(student); + if (newId > 0) { + return Result.ok("更新成功", newId); + } else { + return Result.fail("更新失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Integer id, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + + try { + int newId = studentDAO.deleteById(id); + if (newId > 0) { + return Result.ok("删除成功", newId); + } else { + return Result.fail("删除失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java b/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java deleted file mode 100644 index a1c3ea18..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.link.api.controller; - -import com.link.api.context.DataSourceContextHolder; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/test") -public class TestController { - @Resource - JdbcTemplate jdbcTemplate; - - @GetMapping - public String test() { - return "test"; - } - - @GetMapping("/test1") - public List> test1() { - // 切换数据源为 db1 - DataSourceContextHolder.setDataSourceKey("db1"); - - String sql = "SELECT * FROM user_account"; - - // 执行查询,返回List,每个Map代表一行记录,列名-值对 - List> result = jdbcTemplate.queryForList(sql); - - // 查询完后可以清理上下文,防止影响后续操作 - DataSourceContextHolder.clearDataSourceKey(); - - return result; - } - - @GetMapping("/test2") - public List> test2() { - // 切换数据源为 db1 - DataSourceContextHolder.setDataSourceKey("db2"); - - String sql = "SELECT * FROM sys_admin"; - // 执行查询,返回List,每个Map代表一行记录,列名-值对 - List> result = jdbcTemplate.queryForList(sql); - // 查询完后可以清理上下文,防止影响后续操作 - DataSourceContextHolder.clearDataSourceKey(); - - return result; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java b/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java new file mode 100644 index 00000000..2c682b86 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java @@ -0,0 +1,25 @@ +package com.link.api.controller; +import com.link.api.util.QueryTreeUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/api") +public class TreeController { + + @GetMapping("/tree") + public Result getTreeNodes(@RequestParam String tableName, + @RequestParam String id,@RequestParam int js) { + try { + String orgId = UserContext.getOrgId(); + String json = QueryTreeUtils.getTreeNodes(orgId,tableName,id, js); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java b/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java new file mode 100644 index 00000000..bf488458 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java @@ -0,0 +1,16 @@ +package com.link.api.controller; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.*; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("http://localhost:5173", "http://localhost:8089","http://localhost:5174") // 前端地址 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java b/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java deleted file mode 100644 index c7c20f3f..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.link.api.enums; - -import com.link.api.constants.ResponseStatusConstants; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public enum ResponseEnum { - SUCCESS(ResponseStatusConstants.SUCCESS,"请求成功"), - ERROR(ResponseStatusConstants.ERROR,"服务器错误,请联系管理人员"), - AUTH_SUCCESS(ResponseStatusConstants.SUCCESS,"登录成功"), - AUTH_ERROR(ResponseStatusConstants.ERROR,"登录失败"), - AUTH_EXPIRE(ResponseStatusConstants.UNAUTHORIZED,"尚未登录或登录已过期,请重新登录"); - - private final Integer code; //响应码 - private final String message; //相应信息 -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java b/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java deleted file mode 100644 index 4d14a93d..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.link.api.exception; - -import com.link.api.enums.ResponseEnum; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = true) -@Data -public class BaseException extends RuntimeException { - private final boolean showDetail; - private final String message; - private final Integer code; - private final ResponseEnum responseEnum; - - public BaseException(String message) { - this(message, false, null, null); - } - - public BaseException(String message, Integer code) { - this(message, false, code, null); - } - - public BaseException(Integer code, String message) { - this(message, false, code, null); - } - - // 3. 传 ResponseEnum,默认 showDetail=false - public BaseException(ResponseEnum responseEnum) { - super(responseEnum.getMessage()); - this.message = responseEnum.getMessage(); - this.showDetail = false; - this.code = responseEnum.getCode(); - this.responseEnum = responseEnum; - } - - public BaseException(String message, boolean showDetail) { - this(message, showDetail, null, null); - } - - public BaseException(String message, Integer code, boolean showDetail) { - this(message, showDetail, code, null); - } - - public BaseException(ResponseEnum responseEnum, boolean showDetail) { - super(responseEnum.getMessage()); - this.message = responseEnum.getMessage(); - this.showDetail = showDetail; - this.code = responseEnum.getCode(); - this.responseEnum = responseEnum; - } - - private BaseException(String message, boolean showDetail, Integer code, ResponseEnum responseEnum) { - super(message); - this.message = message; - this.showDetail = showDetail; - this.code = code; - this.responseEnum = responseEnum; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java b/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java deleted file mode 100644 index 6d78731a..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.link.api.exception; - -import com.link.api.model.Result; -import com.link.api.exception.auth.AuthException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; - -@Slf4j -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(ConstraintViolationException.class) - public Result handleConstraintViolationException(ConstraintViolationException ex) { - // 取第一个错误消息 - String errorMessage = ex.getConstraintViolations().stream() - .map(ConstraintViolation::getMessage) - .findFirst() - .orElse("参数校验失败"); - return Result.error(errorMessage); - } - - @ExceptionHandler(AuthException.class) - public Result handleAuthException(AuthException ex){ - return Result.error(ex.getMessage()); - } - -// @ExceptionHandler(Exception.class) -// public Result handleException(Exception ex, HttpServletRequest request) { -// ExceptionDetailInfo info = ExceptionUtils.extractExceptionInfo(ex, request, "com.email"); -// log.error("异常发生在 {}#{}({}:{}),ID: {},信息: {}", -// info.getClassName(), -// info.getMethodName(), -// info.getFileName(), -// info.getLineNumber(), -// info.getId(), -// info.getSummary()); -// ex.printStackTrace(); -// return Result.error(info).withShowDetail(true); -// } - -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java b/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java deleted file mode 100644 index c948fcc0..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.link.api.exception.auth; - - -import com.link.api.enums.ResponseEnum; -import com.link.api.exception.BaseException; - -public class AuthException extends BaseException { - public AuthException() { - super(ResponseEnum.AUTH_EXPIRE); // 调用父类的默认构造函数 - } - - public AuthException(ResponseEnum responseEnum){ - super(responseEnum); - } - - public AuthException(Integer code,String message){ - super(code,message); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java b/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java new file mode 100644 index 00000000..7eed3974 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java @@ -0,0 +1,51 @@ +package com.link.api.manager; + +import io.netty.channel.Channel; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class UserManager { + private static final Map userChannels = new ConcurrentHashMap<>(); + private static final Map channelUsers = new ConcurrentHashMap<>(); + + public static boolean login(String username, Channel channel) { + if (username == null || username.trim().isEmpty() || userChannels.containsKey(username)) { + return false; + } + userChannels.put(username, channel); + channelUsers.put(channel, username); + return true; + } + + public static void logout(Channel channel) { + String username = channelUsers.remove(channel); + if (username != null) { + userChannels.remove(username); + } + } + + public static Channel getUserChannel(String username) { + return userChannels.get(username); + } + + public static String getUsername(Channel channel) { + return channelUsers.get(channel); + } + + public static Set getOnlineUsers() { + return new HashSet<>(userChannels.keySet()); + } + + public static Collection getAllChannels() { + return new ArrayList<>(userChannels.values()); + } + + public static boolean isUserOnline(String username) { + return userChannels.containsKey(username); + } + + public static int getOnlineCount() { + return userChannels.size(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java b/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java new file mode 100644 index 00000000..7a2552dc --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java @@ -0,0 +1,59 @@ +package com.link.api.model; + +import java.util.List; + +public class ChatMessage { + private String type; + private String username; + private String content; + private String targetUser; + private List onlineUsers; + private long timestamp; + + public ChatMessage() { + this.timestamp = System.currentTimeMillis(); + } + + public ChatMessage(String type, String username, String content) { + this(); + this.type = type; + this.username = username; + this.content = content; + } + + public ChatMessage(String type, String username, String content, String targetUser) { + this(type, username, content); + this.targetUser = targetUser; + } + + // Getter和Setter + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public String getUsername() { return username; } + public void setUsername(String username) { this.username = username; } + + public String getContent() { return content; } + public void setContent(String content) { this.content = content; } + + public String getTargetUser() { return targetUser; } + public void setTargetUser(String targetUser) { this.targetUser = targetUser; } + + public List getOnlineUsers() { return onlineUsers; } + public void setOnlineUsers(List onlineUsers) { this.onlineUsers = onlineUsers; } + + public long getTimestamp() { return timestamp; } + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + + @Override + public String toString() { + return "ChatMessage{" + + "type='" + type + '\'' + + ", username='" + username + '\'' + + ", content='" + content + '\'' + + ", targetUser='" + targetUser + '\'' + + ", onlineUsers=" + onlineUsers + + ", timestamp=" + timestamp + + '}'; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java b/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java deleted file mode 100644 index e8ed5c23..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.link.api.model; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import lombok.Builder; -import lombok.Data; - -import java.util.Date; -@Data -@Builder -public class ExceptionEntity { - /** 异常记录的唯一ID */ - @JsonSerialize(using = ToStringSerializer.class) - private Long id; - - /** 异常发生时间 */ - private Date time; - - /** 请求的URI地址 */ - private String uri; - - /** HTTP请求方法,如GET、POST等 */ - private String httpMethod; - - /** 抛出异常的类名 */ - private String className; - - /** 抛出异常的方法名 */ - private String methodName; - - /** 抛出异常的文件名 */ - private String fileName; - - /** 抛出异常的代码行号 */ - private Integer lineNumber; - - /** 异常的简短信息 */ - private String summary; - - /** 异常的详细堆栈信息 */ - private String detailMessage; - - /** 请求参数(可能为JSON字符串等) */ - private String params; -} diff --git a/code/link/link-backend/src/main/java/com/link/api/model/Result.java b/code/link/link-backend/src/main/java/com/link/api/model/Result.java deleted file mode 100644 index 5d6db0f1..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/model/Result.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.link.api.model; - -import com.link.api.enums.ResponseEnum; -import lombok.NonNull; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder(toBuilder = true) -public class Result { - private final Integer code; - private final String message; - private final T data; - private final Boolean showExceptionDetail; - - // 工厂方法建议保留,简化构造调用 - public static Result success() { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(ResponseEnum.SUCCESS.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result success(String message) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result success(@NonNull T data) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(ResponseEnum.SUCCESS.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result success(@NonNull T data, String message) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(message) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result success(ResponseEnum responseEnum) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result success(ResponseEnum responseEnum, @NonNull T data) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result error() { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(ResponseEnum.ERROR.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result error(String message) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result error(Integer code, String message) { - return Result.builder() - .code(code) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result error(ResponseEnum responseEnum) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result error(T data) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(ResponseEnum.ERROR.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result error(T data, String message) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(message) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public Result withShowExceptionDetail(boolean show) { - return this.toBuilder().showExceptionDetail(show).build(); - } - - public boolean isSuccess() { - return ResponseEnum.SUCCESS.getCode().equals(this.code); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/model/Student.java b/code/link/link-backend/src/main/java/com/link/api/model/Student.java new file mode 100644 index 00000000..95dc155b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/Student.java @@ -0,0 +1,41 @@ +package com.link.api.model; + +public class Student { + private Integer b_id; + private String b_name; + private Integer b_age; + private String b_class; + + public Integer getB_id() { + return b_id; + } + + public void setB_id(Integer b_id) { + this.b_id = b_id; + } + + public String getB_name() { + return b_name; + } + + public void setB_name(String b_name) { + this.b_name = b_name; + } + + public Integer getB_age() { + return b_age; + } + + public void setB_age(Integer b_age) { + this.b_age = b_age; + } + + public String getB_class() { + return b_class; + } + + public void setB_class(String b_class) { + this.b_class = b_class; + } + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java b/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java new file mode 100644 index 00000000..bc4f6b30 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java @@ -0,0 +1,33 @@ +package com.link.api.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserInfo { + private final String userId; + private final String username; + private final String password; + + @JsonCreator + public UserInfo( + @JsonProperty("userId") String userId, + @JsonProperty("username") String username, + @JsonProperty("password") String password + ) { + this.userId = userId; + this.username = username; + this.password = password; + } + + public String getUserId() { + return userId; + } + + public String getPassword() { + return password; + } + + public String getUsername() { + return username; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java b/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java new file mode 100644 index 00000000..16919331 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java @@ -0,0 +1,53 @@ +package com.link.api.repository; + +import com.link.api.util.DruidDataSourceManager; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class MultiTenantUserDao { + + public static void insert(String orgCode, String username, String password) throws SQLException { + String sql = "INSERT INTO chat_users (username, password) VALUES (?, ?)"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, username); + ps.setString(2, password); + ps.executeUpdate(); + } + } + + public static List list(String orgCode) throws SQLException { + String sql = "SELECT id, username, password FROM chat_users"; + List result = new ArrayList<>(); + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + result.add(rs.getInt("id") + ": " + rs.getString("username") + ", " + rs.getString("password")); + } + } + return result; + } + + public static void update(String orgCode, int id, String username, String password) throws SQLException { + String sql = "UPDATE chat_users SET username = ?, password = ? WHERE id = ?"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, username); + ps.setString(2, password); + ps.setInt(3, id); + ps.executeUpdate(); + } + } + + public static void delete(String orgCode, int id) throws SQLException { + String sql = "DELETE FROM chat_users WHERE id = ?"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, id); + ps.executeUpdate(); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java new file mode 100644 index 00000000..c2b339ef --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java @@ -0,0 +1,36 @@ +package com.link.api.repository; + +import com.link.api.model.Student; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class StudentDAO { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public List findAll() { + String sql = "SELECT * FROM Students"; + return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class)); + } + + public int insert(Student s) { + String sql = "INSERT INTO Students (b_name, b_age, b_class) VALUES (?, ?, ?)"; + return jdbcTemplate.update(sql, s.getB_name(), s.getB_age(), s.getB_class()); + } + + public int update(Student s) { + String sql = "UPDATE Students SET b_name = ?, b_age = ?, b_class = ? WHERE b_id = ?"; + return jdbcTemplate.update(sql, s.getB_name(), s.getB_age(), s.getB_class(), s.getB_id()); + } + + public int deleteById(int id) { + String sql = "DELETE FROM Students WHERE b_id = ?"; + return jdbcTemplate.update(sql, id); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java new file mode 100644 index 00000000..8f81398b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java @@ -0,0 +1,88 @@ +package com.link.api.repository; + +import com.link.api.model.Student; +import com.link.api.util.ConnectionManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +@Repository +public class StudentDaoJDBC { + + @Autowired + private ConnectionManager connectionManager; + + public List findAll() { + List list = new ArrayList<>(); + String sql = "SELECT b_id, b_name, b_age, b_class FROM Students"; + + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + while (rs.next()) { + Student s = new Student(); + s.setB_id(rs.getInt("b_id")); + s.setB_name(rs.getString("b_name")); + s.setB_age(rs.getInt("b_age")); + s.setB_class(rs.getString("b_class")); + list.add(s); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return list; + } + + public int insert(Student s) { + String sql = "INSERT INTO Students (b_name, b_age, b_class) VALUES (?, ?, ?)"; + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, s.getB_name()); + stmt.setInt(2, s.getB_age()); + stmt.setString(3, s.getB_class()); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + public int update(Student s) { + String sql = "UPDATE Students SET b_name = ?, b_age = ?, b_class = ? WHERE b_id = ?"; + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, s.getB_name()); + stmt.setInt(2, s.getB_age()); + stmt.setString(3, s.getB_class()); + stmt.setInt(4, s.getB_id()); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + public int deleteById(int id) { + String sql = "DELETE FROM Students WHERE b_id = ?"; + + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setInt(1, id); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java b/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java new file mode 100644 index 00000000..e7a9fa91 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java @@ -0,0 +1,42 @@ +package com.link.api.repository; + +import com.link.api.model.UserInfo; +import com.link.api.util.DruidDataSourceManager; + +import java.sql.*; + +public class UserRepository { + + /** + * 根据 orgCode 和 userId 查询用户信息 + * + * @param orgCode 多租户的 orgCode + * @param userId 用户 ID + * @return 用户信息,如果不存在返回 null + */ + public static UserInfo getUserPasswordById(String orgCode, String userId) { + String sql = "SELECT b_id,b_name, b_password FROM b_user WHERE b_id = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, userId); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + String id = rs.getString("b_id"); + String username = rs.getString("b_name"); + String password = rs.getString("b_password"); + System.out.println("username=---------------"); + System.out.println(username); + return new UserInfo(id,username, password); + } else { + return null; + } + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java new file mode 100644 index 00000000..1064f75e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java @@ -0,0 +1,126 @@ +package com.link.api.util; + + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.springframework.jdbc.support.JdbcUtils.closeStatement; + +public class BatchColumn { + + public static Result postSaveColumn(String orgCode, List> columnMaps) { + + Connection conn = null; + PreparedStatement pstmt = null; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + String sql = "UPDATE s_columnlib SET col_Width = ?,col_Position=? WHERE col_TableName = ? and col_FieldName = ?"; + pstmt = conn.prepareStatement(sql); + + System.out.println(columnMaps.toString()); + + int i = 2; + for (Map row : columnMaps) { + // 从 Map 取值(要确保 key 和数据库字段对应) + int width = (int) row.get("width"); + String tableName = (String) row.get("col_TableName"); + Object fieldName = row.get("prop"); + + pstmt.setInt(1, width); + pstmt.setInt(2, i); + pstmt.setString(3, tableName); + pstmt.setObject(4, fieldName); + + pstmt.addBatch(); + i += 2; + } + + pstmt.executeBatch(); + conn.commit(); + + System.out.println("批量更新成功,处理了 " + columnMaps.size() + " 条数据"); + return Result.ok("保存成功"); + } catch (SQLException e) { + rollbackTransaction(conn); + e.printStackTrace(); + return Result.fail("保存失败: " + e.getMessage()); + } finally { + closeStatement(pstmt); + closeConnection(conn); + } + } + + public static Result postSaveColumnEdit(String orgCode, List> columnMaps) { + + Connection conn = null; + PreparedStatement pstmt = null; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + String sql = "UPDATE s_columnlib SET col_edit_width = ?,col_edit_position=? WHERE col_TableName = ? and col_FieldName = ?"; + pstmt = conn.prepareStatement(sql); + + System.out.println(columnMaps.toString()); + + int i = 2; + for (Map row : columnMaps) { + // 从 Map 取值(要确保 key 和数据库字段对应) + int width = (int) row.get("edit_width"); + String tableName = (String) row.get("col_TableName"); + Object fieldName = row.get("prop"); + + pstmt.setInt(1, width); + pstmt.setInt(2, i); + pstmt.setString(3, tableName); + pstmt.setObject(4, fieldName); + + pstmt.addBatch(); + i += 2; + } + + pstmt.executeBatch(); + conn.commit(); + + System.out.println("批量更新成功,处理了 " + columnMaps.size() + " 条数据"); + return Result.ok("保存成功"); + } catch (SQLException e) { + rollbackTransaction(conn); + e.printStackTrace(); + return Result.fail("保存失败: " + e.getMessage()); + } finally { + closeStatement(pstmt); + closeConnection(conn); + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java new file mode 100644 index 00000000..7ee4cbae --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java @@ -0,0 +1,269 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTable { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static Result> batchChanges( + String orgCode, + String[] tableNames, + String[] keyFields, + String[] inserts, // JSON字符串数组 + String[] updates, + String[] deletes + ) { + // 验证参数 + if (tableNames == null || keyFields == null || inserts == null || updates == null || deletes == null) { + return Result.fail("参数不能为null"); + } + + Connection conn = null; + Map resultStats = new HashMap<>(); + resultStats.put("totalTables", tableNames.length); + int totalInserts = 0; + int totalUpdates = 0; + int totalDeletes = 0; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + for (int i = 0; i < tableNames.length; i++) { + String table = tableNames[i]; + String key = keyFields[i]; + + // 安全检查:避免越界 + String insertJson = i < inserts.length ? inserts[i] : "[]"; + String updateJson = i < updates.length ? updates[i] : "[]"; + String deleteJson = i < deletes.length ? deletes[i] : "[]"; + + List> insertList = parseJsonArray(insertJson); + List> updateList = parseJsonArray(updateJson); + List> deleteList = parseJsonArray(deleteJson); + + // 执行插入操作 + for (int j = 0; j < insertList.size(); j++) { + insert(conn, table, insertList.get(j)); + } + totalInserts += insertList.size(); + + // 执行更新操作 + for (int j = 0; j < updateList.size(); j++) { + updateex(conn, table, updateList.get(j), key); + } + totalUpdates += updateList.size(); + + // 执行删除操作 + for (int j = 0; j < deleteList.size(); j++) { + delete(conn, table, deleteList.get(j), key); + } + totalDeletes += deleteList.size(); + } + + conn.commit(); + + // 构建统计结果 + resultStats.put("insertedRecords", totalInserts); + resultStats.put("updatedRecords", totalUpdates); + resultStats.put("deletedRecords", totalDeletes); + + return Result.ok("批量操作成功", resultStats); + + } catch (SQLException e) { + try { + if (conn != null) conn.rollback(); + } catch (SQLException ex) { + return Result.fail("批量操作失败且回滚失败: " + ex.getMessage()); + } + return Result.fail("批量操作失败,事务已回滚: " + e.getMessage()); + } catch (Exception e) { + try { + if (conn != null) conn.rollback(); + } catch (SQLException ex) { + return Result.fail("批量操作失败且回滚失败: " + ex.getMessage()); + } + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + // 关闭连接时的异常不影响业务结果,可以记录日志 + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + } + + + private static List> parseJsonArray(String json) throws Exception { + if (json == null || json.trim().isEmpty()) return Collections.emptyList(); + + JsonNode node = mapper.readTree(json); + + if (node.isArray()) { + return mapper.readValue(json, new TypeReference>>() { + }); + } else if (node.isObject()) { + Map single = mapper.readValue(json, new TypeReference>() { + }); + return Collections.singletonList(single); // 包成 List 返回 + } else { + throw new IllegalArgumentException("Invalid JSON input: expected array or object"); + } + } + + private static void insert(Connection conn, String table, Map data) throws Exception { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, table, insertFields); + System.out.println("[SQL-INSERT-buildInsertSql] " + sql); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(java.util.stream.Collectors.toList())); + } + } + + private static void update(Connection conn, String table, Map row, String pkField) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, table, pkField, updateFields); // 构建 SQL 并获取字段顺序 + + List values = new ArrayList<>(); + for (String field : updateFields) { + Object value = row.get(field); + if (field.equalsIgnoreCase(pkField)) { + if (value == null) throw new IllegalArgumentException("更新时主键不能为空:" + pkField); + } + values.add(value); + } + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + row); + } + } + } + + private static void delete(Connection conn, String table, Map row, String pkField) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(table, pkField); + + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + } + } + + + private static void updateex(Connection conn, String table, Map row, String pkField) throws SQLException { + // 检查主键是否存在 + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + // 排除主键字段,只获取需要更新的字段 + Set changedFields = row.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + row); + } + + // 构建动态SQL + String sql = buildDynamicUpdateSql(table, pkField, changedFields); + + // 准备参数值 + List values = new ArrayList<>(); + for (String field : changedFields) { + values.add(row.get(field)); + } + // 最后添加主键值作为WHERE条件 + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + row); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + * + * @param table 表名 + * @param pkField 主键字段名 + * @param fieldsToUpdate 需要更新的字段集合 + * @return 生成的SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + // 添加要更新的字段 + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + // 添加WHERE条件 + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java new file mode 100644 index 00000000..6075bbee --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java @@ -0,0 +1,198 @@ +package com.link.api.util; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + + +public class BatchTableDelete { + private static final ObjectMapper mapper = new ObjectMapper(); + + // === 入口 === + public static Result> executdeletetable(String orgCode, String moduleid, String ID) throws SQLException { + boolean candelete = deletebefore(orgCode, moduleid, ID); + if (candelete) { + List> deletetablelist = getDeletetableList(orgCode, moduleid, ID); + return deleteDatabaseOperations(orgCode, deletetablelist); + } + return Result.fail("存在依赖数据,禁止删除: table=" + moduleid + ", id=" + ID); + } + + // === 删除前检查依赖 === + public static boolean deletebefore(String orgCode, String moduleid, String ID) throws SQLException { + String sql = "SELECT b_JudgeCondition FROM s_module WHERE b_id = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, moduleid); + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + String conditionSql = rs.getString("b_JudgeCondition"); + + if (conditionSql == null || conditionSql.trim().isEmpty()) { + return true; // 没配置依赖条件,默认允许删除 + } + + // 替换占位符 + conditionSql = conditionSql.replace(":b_id", "'" + ID + "'"); + System.out.println("[执行条件SQL] " + conditionSql); + + try (PreparedStatement ps = conn.prepareStatement(conditionSql); + ResultSet crs = ps.executeQuery()) { + + if (crs.next()) { + System.out.println("存在依赖数据,不允许删除: ID=" + ID); + return false; + } else { + System.out.println("未发现依赖数据,可以删除: ID=" + ID); + return true; + } + } + } + } + } catch (Exception e) { + throw new RuntimeException("执行 deletebefore 失败", e); + } + + return false; // 默认不允许删除 + } + + // === 获取需要删除的表集合 === + public static List> getDeletetableList(String orgCode, String moduleid, String ID) throws SQLException { + String sql = "SELECT b_SaveTable FROM s_module WHERE b_LinkTable = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, moduleid); + + try (ResultSet rs = stmt.executeQuery()) { + List> tablelist = getlinktablelist(rs, ID); + + // 添加主表 + Map map = new LinkedHashMap<>(); + map.put("tableName", moduleid); + map.put("keyField", "b_id"); + map.put("Fieldvalue", ID); + tablelist.add(map); + + return tablelist; + } + } catch (Exception e) { + throw new RuntimeException("查询 s_module 失败", e); + } + } + + private static List> getlinktablelist(ResultSet rs, String ID) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("tableName", rs.getString("b_SaveTable")); + map.put("keyField", "father"); + map.put("Fieldvalue", ID); + list.add(map); + } + return list; + } + + // === 执行批量删除 === + public static Result> deleteDatabaseOperations(String orgCode, List> tableMaps) { + Map operationCounts = new LinkedHashMap<>(); + operationCounts.put("total", 0); + + Connection conn = null; + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + String fieldValue = (String) tablemap.get("Fieldvalue"); + + validateIdentifier(tableName); + validateIdentifier(keyField); + + int count = processDeleteOperations(conn, tableName, keyField, fieldValue); + + // 每个表单独统计 + operationCounts.merge(tableName, count, Integer::sum); + // 总数统计 + operationCounts.merge("total", count, Integer::sum); + + System.out.println("表 [" + tableName + "] 删除记录数: " + count); + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // === 删除逻辑 === + private static int processDeleteOperations(Connection conn, String tableName, String keyField, String fieldValue) throws SQLException { + return deleteRecord(conn, tableName, keyField, fieldValue); + } + + private static int deleteRecord(Connection conn, String tableName, String pkField, String pkFieldvalue) throws SQLException { + if (pkFieldvalue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " 值为空"); + } + + String sql = getDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkFieldvalue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkFieldvalue); + int affected = ps.executeUpdate(); + if (affected == 0) { + System.out.println("未找到要删除的记录: " + pkFieldvalue); + } + return affected; + } + } + + // === 辅助方法 === + private static void validateIdentifier(String identifier) { + if (!identifier.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名: " + identifier); + } + } + + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + public static String getDeleteSql(String tableName, String pkName) { + return "DELETE FROM " + tableName + " WHERE " + pkName + " = ?"; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java new file mode 100644 index 00000000..1b0fa5c9 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java @@ -0,0 +1,280 @@ +package com.link.api.util; + +import java.sql.*; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV2 { + + // 字段类型缓存:避免每次都查元数据 + private static final Map> columnTypeCache = new HashMap<>(); + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + Map columnTypes = getColumnTypes(conn, tableName); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + String field = insertFields.get(i); + String colType = columnTypes.getOrDefault(field.toLowerCase(), "varchar"); + Object value = convertValueByColumnType(colType, data.get(field)); + ps.setObject(i + 1, value); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + Set changedFields = data.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + data); + } + String sql = buildDynamicUpdateSql(tableName, pkField, changedFields); + Map columnTypes = getColumnTypes(conn, tableName); + + List values = new ArrayList<>(); + for (String field : changedFields) { + String colType = columnTypes.getOrDefault(field.toLowerCase(), "varchar"); + values.add(convertValueByColumnType(colType, data.get(field))); + } + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } + + /** + * 获取表字段类型(带缓存) + */ + private static Map getColumnTypes(Connection conn, String tableName) throws SQLException { + if (columnTypeCache.containsKey(tableName)) { + return columnTypeCache.get(tableName); + } + + Map columnTypes = new HashMap<>(); + String sql = "SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, tableName); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + columnTypes.put(rs.getString("COLUMN_NAME").toLowerCase(), + rs.getString("DATA_TYPE").toLowerCase()); + } + } + } + columnTypeCache.put(tableName, columnTypes); + return columnTypes; + } + + /** + * 根据字段类型转换值(数字字段:空字符串转0) + */ + private static Object convertValueByColumnType(String columnType, Object value) { + if (value == null) return null; + + if (value instanceof String && ((String) value).trim().isEmpty()) { + if (isNumericType(columnType)) { + return 0; + } + } + + return value; + } + + /** + * 判断是否为数字类型 + */ + private static boolean isNumericType(String columnType) { + return columnType.contains("int") + || columnType.contains("decimal") + || columnType.contains("numeric") + || columnType.contains("float") + || columnType.contains("real") + || columnType.contains("money"); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java new file mode 100644 index 00000000..707c492b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java @@ -0,0 +1,201 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class BatchTableV220250829 { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, tableName, pkField, updateFields); + List values = updateFields.stream() + .map(data::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java new file mode 100644 index 00000000..dfc725b2 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java @@ -0,0 +1,238 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV2bk { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + // 排除主键字段,只获取需要更新的字段 + Set changedFields = data.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + data); + } + + // 构建动态SQL + String sql = buildDynamicUpdateSql(tableName, pkField, changedFields); + + + // 准备参数值 + List values = new ArrayList<>(); + for (String field : changedFields) { + values.add(data.get(field)); + } + // 最后添加主键值作为WHERE条件 + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + * + * @param table 表名 + * @param pkField 主键字段名 + * @param fieldsToUpdate 需要更新的字段集合 + * @return 生成的SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + // 添加要更新的字段 + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + // 添加WHERE条件 + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java new file mode 100644 index 00000000..7b0aeb98 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java @@ -0,0 +1,209 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV3 { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + @SuppressWarnings("unchecked") + List keyFields = (List) tablemap.get("keyFields"); // ✅ 多主键字段 + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyFields, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyFields, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作(支持多主键) + private static int processUpdateOperations(Connection conn, String tableName, List keyFields, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyFields, record); + } + return totalUpdated; + } + + // 处理删除操作(支持多主键) + private static int processDeleteOperations(Connection conn, String tableName, List keyFields, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyFields, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录(支持多主键) + private static int updateRecord(Connection conn, String tableName, List pkFields, + Map data) throws SQLException { + for (String pkField : pkFields) { + if (!data.containsKey(pkField)) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, tableName, pkFields, data, updateFields); + + // 直接按 outFields 获取参数 + List values = updateFields.stream() + .map(data::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + + // 删除单条记录(支持多主键) + private static int deleteRecord(Connection conn, String tableName, List pkFields, + Map row) throws SQLException { + for (String pkField : pkFields) { + if (!row.containsKey(pkField)) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + } + + String sql = BuildSql.buildDeleteSql(tableName, pkFields); // ✅ 支持多主键 + List values = pkFields.stream() + .map(row::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + // 如果没有匹配记录,不再抛异常,而是直接返回 0 + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java b/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java new file mode 100644 index 00000000..7be6491e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java @@ -0,0 +1,186 @@ +package com.link.api.util; + +import java.sql.*; +import java.util.*; + +public class BuildSql { + + // 保留类型排除列表:部分字段可能用到 text + private static final Set EXCLUDED_TYPES = new HashSet<>(Arrays.asList( +// "image", "text", "timestamp", "uniqueidentifier" + "image", "timestamp", "uniqueidentifier" + )); + + /** + * 生成 INSERT SQL,例如:INSERT INTO table (col1, col2) VALUES (?, ?) + * @param conn 数据库连接 + * @param tableName 表名 + * @param outFields 输出参数,用于接收有效字段名(按顺序) + * @return 生成的 INSERT SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildInsertSql(Connection conn, String tableName, List outFields) throws SQLException { + return buildSqlWithFields(conn, tableName, outFields, "insert"); + } + + /** + * 生成 UPDATE SQL,例如:UPDATE table SET col1=?, col2=? WHERE pkName=? + * @param conn 数据库连接 + * @param tableName 表名 + * @param pkName 主键字段名 + * @param outFields 输出参数,用于接收有效字段名(按顺序,最后是主键) + * @return 生成的 UPDATE SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildUpdateSql(Connection conn, String tableName, String pkName, List outFields) throws SQLException { + StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET "); + List fields = getValidFields(conn, tableName); + + for (String col : fields) { + if (!col.equalsIgnoreCase(pkName)) { + sql.append(col).append(" = ?, "); + outFields.add(col); + } + } + + if (outFields.isEmpty()) throw new SQLException("无可更新字段"); + + sql.setLength(sql.length() - 2); // 去掉最后多余的逗号和空格 + sql.append(" WHERE ").append(pkName).append(" = ?"); + outFields.add(pkName); // 主键放到最后 + + return sql.toString(); + } + + /** + * 支持多主键,生成 UPDATE SQL,例如:UPDATE table SET col1=?, col2=? WHERE pk1=? AND pk2=? + * + * @param conn 数据库连接 + * @param tableName 表名 + * @param pkNames 多主键字段名列表 + * @param outFields 输出参数,用于接收有效字段名(按顺序,最后是主键) + * @return 生成的 UPDATE SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildUpdateSql(Connection conn, String tableName, List pkNames, Map data, List outFields) throws SQLException { + if (pkNames == null || pkNames.isEmpty()) { + throw new SQLException("缺少主键字段"); + } + + StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET "); + List fields = getValidFields(conn, tableName); + + boolean hasUpdateField = false; + + // 拼接非主键字段,只更新 data 中存在的字段 + for (String col : fields) { + boolean isPk = pkNames.stream().anyMatch(pk -> pk.equalsIgnoreCase(col)); + if (!isPk && data.containsKey(col)) { + sql.append(col).append(" = ?, "); + outFields.add(col); + hasUpdateField = true; + } + } + + if (!hasUpdateField) { + throw new SQLException("没有可更新字段"); + } + + sql.setLength(sql.length() - 2); // 去掉最后逗号和空格 + + // 拼接 WHERE 多主键条件 + sql.append(" WHERE "); + for (int i = 0; i < pkNames.size(); i++) { + String pk = pkNames.get(i); + sql.append(pk).append(" = ?"); + if (i < pkNames.size() - 1) sql.append(" AND "); + outFields.add(pk); + } + + return sql.toString(); + } + + /** + * 生成 DELETE SQL,例如:DELETE FROM table WHERE pkName=? + * @param tableName 表名 + * @param pkName 主键字段名 + * @return 生成的 DELETE SQL + */ + public static String buildDeleteSql(String tableName, String pkName) { + return "DELETE FROM " + tableName + " WHERE " + pkName + " = ?"; + } + + /** + * 生成 DELETE SQL,例如: + * 单主键: DELETE FROM table WHERE id = ? + * 多主键: DELETE FROM table WHERE id = ? AND org_code = ? + * + * @param tableName 表名 + * @param pkNames 主键字段列表 + * @return 生成的 DELETE SQL + */ + public static String buildDeleteSql(String tableName, List pkNames) throws SQLException { + if (pkNames == null || pkNames.isEmpty()) { + throw new SQLException("缺少主键字段"); + } + + StringBuilder sql = new StringBuilder("DELETE FROM ").append(tableName).append(" WHERE "); + + for (int i = 0; i < pkNames.size(); i++) { + sql.append(pkNames.get(i)).append(" = ?"); + if (i < pkNames.size() - 1) { + sql.append(" AND "); + } + } + + return sql.toString(); + } + + /** + * 获取可插入字段(排除不支持的类型或自增字段) + */ + private static List getValidFields(Connection conn, String tableName) throws SQLException { + List fields = new ArrayList<>(); + String dummyQuery = "SELECT * FROM " + tableName + " WHERE 1=0"; + + try (PreparedStatement ps = conn.prepareStatement(dummyQuery); + ResultSet rs = ps.executeQuery()) { + ResultSetMetaData metaData = rs.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + String col = metaData.getColumnName(i); + String type = metaData.getColumnTypeName(i).toLowerCase(); + + // 自动排除自增字段和某些不支持类型 + if (EXCLUDED_TYPES.contains(type) || metaData.isAutoIncrement(i)) continue; + + fields.add(col); + } + } + + return fields; + } + + /** + * 内部方法:构建 INSERT SQL 语句 + */ + private static String buildSqlWithFields(Connection conn, String tableName, List outFields, String type) throws SQLException { + List fields = getValidFields(conn, tableName); + if (fields.isEmpty()) throw new SQLException("无可插入字段"); + + StringBuilder sql = new StringBuilder(); + StringBuilder placeholders = new StringBuilder(); + + sql.append("INSERT INTO ").append(tableName).append(" ("); + for (String col : fields) { + sql.append(col).append(","); + placeholders.append("?,"); + outFields.add(col); + } + + sql.setLength(sql.length() - 1); // 移除最后逗号 + placeholders.setLength(placeholders.length() - 1); + + sql.append(") VALUES (").append(placeholders).append(")"); + return sql.toString(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java b/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java new file mode 100644 index 00000000..59c2ca29 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java @@ -0,0 +1,19 @@ +package com.link.api.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +@Component +public class ConnectionManager { + + @Autowired + private DataSource dataSource; + + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java b/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java new file mode 100644 index 00000000..531091c6 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java @@ -0,0 +1,61 @@ +package com.link.api.util; + + +import com.alibaba.druid.pool.DruidDataSource; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +public class DruidDataSourceManager { + private static final String CONFIG_PATH = "dbconfigs/"; // 放在 resources/dbconfigs 目录下 + private static final Map dataSourceMap = new ConcurrentHashMap<>(); + + public static synchronized Connection getConnection(String orgCode) throws SQLException { + DruidDataSource ds = dataSourceMap.get(orgCode); + if (ds == null) { + ds = createDataSourceFromFile(orgCode); + dataSourceMap.put(orgCode, ds); + } + return ds.getConnection(); + } + + private static DruidDataSource createDataSourceFromFile(String orgCode) { + String fileName = CONFIG_PATH + orgCode + ".properties"; + try (InputStream in = DruidDataSourceManager.class.getClassLoader().getResourceAsStream(fileName)) { + if (in == null) throw new RuntimeException("未找到机构配置文件: " + fileName); + Properties props = new Properties(); + props.load(in); + + DruidDataSource ds = new DruidDataSource(); + ds.setUrl(props.getProperty("url")); + ds.setUsername(props.getProperty("username")); + ds.setPassword(props.getProperty("password")); + ds.setDriverClassName(props.getProperty("driver")); + + // 可选参数 + ds.setInitialSize(3); + ds.setMinIdle(2); + ds.setMaxActive(10); + ds.setMaxWait(5000); + ds.setValidationQuery("SELECT 1"); + ds.setTestWhileIdle(true); + ds.setTestOnBorrow(false); + ds.setTestOnReturn(false); + + return ds; + } catch (Exception e) { + throw new RuntimeException("加载数据库配置失败: " + fileName, e); + } + } + + public static synchronized void closeAll() { + for (DruidDataSource ds : dataSourceMap.values()) { + ds.close(); + } + dataSourceMap.clear(); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java b/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java new file mode 100644 index 00000000..cd22d9d2 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java @@ -0,0 +1,26 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonIMUtil { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static String toJson(Object obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } + + public static T fromJson(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java new file mode 100644 index 00000000..177efea0 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java @@ -0,0 +1,231 @@ +package com.link.api.util; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class JsonUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + + public static List> queryColumnConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName, col_Caption, col_Width FROM s_columnLib WHERE col_TableName = ? and col_Visible= ? order by col_Position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + /** + * 把 ResultSet 映射为 prop/label/width 的字段 Map 列表 + */ + private static List> extractFieldMapList(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + list.add(map); + } + return list; + } + + public static List> queryColumnEditConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName,col_FieldName_v, col_Caption, col_Width, col_edit_type,col_edit_width,col_ReadOnly,col_edit_source_table,col_edit_source_field,col_edit_option FROM s_columnLib WHERE col_TableName = ? and col_edit= ? order by col_edit_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList_edit(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + private static List> extractFieldMapList_edit(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + map.put("edit_type", rs.getObject("col_edit_type")); + map.put("edit_width", rs.getObject("col_edit_width")); + map.put("readOnly", rs.getObject("col_ReadOnly")); + map.put("FieldName_v", rs.getObject("col_FieldName_v")); + map.put("edit_source_table", rs.getObject("col_edit_source_table")); + map.put("edit_source_field", rs.getObject("col_edit_source_field")); + map.put("edit_option", rs.getObject("col_edit_option")); + + + list.add(map); + } + return list; + } + public static List> queryColumnsSelectConfig(String orgCode, String tableName) throws SQLException { + String sql = "select * from s_selectcolumnlib where col_visible='1' and selectid = ? order by col_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList_select(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + private static List> extractFieldMapList_select(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + + list.add(map); + } + return list; + } + + + public static void ColumnConfigJson(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName, col_Caption, col_Width FROM s_columnLib WHERE col_TableName = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + + try (ResultSet rs = stmt.executeQuery()) { + String json = toFieldJsonArray(rs); + System.out.println(json); // 控制台输出 + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + + public static String toFieldJsonArray(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + list.add(map); + } + try { + return mapper.writeValueAsString(list); + } catch (Exception e) { + throw new RuntimeException("转换为字段 JSON 失败", e); + } + } + + + public static String toJsonArray(ResultSet rs) throws SQLException { + List> rows = resultSetToList(rs); + try { + return mapper.writeValueAsString(rows); + } catch (Exception e) { + throw new RuntimeException("转换为 JSON 失败", e); + } + } + + /** + * 将 ResultSet 转为 List> + */ + private static List> resultSetToList(ResultSet rs) throws SQLException { + List> rows = new ArrayList<>(); + ResultSetMetaData metaData = rs.getMetaData(); + int colCount = metaData.getColumnCount(); + + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= colCount; i++) { + String colName = metaData.getColumnLabel(i); // 支持 SQL 别名 + Object value = rs.getObject(i); + row.put(colName, value); + } + rows.add(row); + } + + return rows; + } + + + public static List> querySearchConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_fieldname,col_fieldname_v, col_caption, col_defaultValue,col_type, col_option,col_hit,col_source_table,col_source_field,col_source_option FROM s_search_sub WHERE father = ? and col_visible= ? order by col_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return toSearchFieldJsonArray(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + + private static List> toSearchFieldJsonArray(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + + map.put("label", rs.getObject("col_caption")); + map.put("field", rs.getObject("col_fieldname")); + map.put("fieldname_v", rs.getObject("col_fieldname_v")); + map.put("type", rs.getObject("col_type")); + map.put("value", rs.getObject("col_defaultValue")); + map.put("placeholder", rs.getObject("col_hit")); + map.put("options", rs.getObject("col_option")); + map.put("sourceTable", rs.getObject("col_source_table")); + map.put("sourceTableField", rs.getObject("col_source_field")); + map.put("source_option", rs.getObject("col_source_option")); + + + list.add(map); + } + return list; + } + + + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java new file mode 100644 index 00000000..60529c4d --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java @@ -0,0 +1,78 @@ +package com.link.api.util; + +import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + // 可跳过 Token 校验的路径 + private static final List EXCLUDE_PATHS = Arrays.asList( + "/api/login", "/api/register", "/public", "/health" + ); + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + // 0. 放行 OPTIONS 请求(解决 CORS 预检) + if ("OPTIONS".equals(request.getMethod())) { + response.setStatus(HttpServletResponse.SC_OK); + filterChain.doFilter(request, response); + return; + } + + try { + String path = request.getRequestURI(); + + // 1. 跳过不需要验证的路径 + if (shouldSkip(path)) { + filterChain.doFilter(request, response); + return; + } + + // 2. 获取并验证 Token + String token = request.getHeader("Authorization"); + if (token == null || !token.startsWith("Bearer ")) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Missing or invalid Authorization header"); + return; + } + + token = token.substring(7); // 去掉 "Bearer " + JwtPayload payload = JwtUtil.parseToken(token); // 你的 JWT 解析方法 + if (payload == null) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); + return; + } + + // 3. 保存用户上下文 + UserContext.set(payload.getUserid(), payload.getOrgid()); + + // 4. 放行请求 + filterChain.doFilter(request, response); + + } catch (Exception e) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Token authentication failed: " + e.getMessage()); + } finally { + UserContext.clear(); // 清理上下文 + } + } + + // 判断是否跳过路径 + private boolean shouldSkip(String path) { + return EXCLUDE_PATHS.stream().anyMatch(path::startsWith); + } + + // 统一返回错误响应 + private void sendError(HttpServletResponse response, int status, String message) throws IOException { + response.setStatus(status); + response.setContentType("application/json"); + response.getWriter().write("{\"error\": \"" + message + "\"}"); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java new file mode 100644 index 00000000..d7a8d80a --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java @@ -0,0 +1,29 @@ +package com.link.api.util; + +public class JwtPayload { + private String userid; + private String orgid; + + public JwtPayload() {} + + public JwtPayload(String userid, String orgid) { + this.userid = userid; + this.orgid = orgid; + } + + public String getUserid() { + return userid; + } + + public String getOrgid() { + return orgid; + } + + public void setUserid(String userid) { + this.userid = userid; + } + + public void setOrgid(String orgid) { + this.orgid = orgid; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java new file mode 100644 index 00000000..a02ec1e6 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java @@ -0,0 +1,38 @@ +package com.link.api.util; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Date; + +public class JwtUtil { + private static final String SECRET_KEY = "your_secret_key"; + private static final long EXPIRATION = 86400000L; // 1天 + + public static String generateToken(String username,String orgid) { + return Jwts.builder() + .setSubject(username) + .claim("orgid", orgid) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) + .signWith(SignatureAlgorithm.HS256, SECRET_KEY) + .compact(); + } + + public static JwtPayload parseToken(String token) { + try { + Claims claims = Jwts.parser() + .setSigningKey(SECRET_KEY) + .parseClaimsJws(token) + .getBody(); + + String username = claims.getSubject(); + String orgid = claims.get("orgid", String.class); + + return new JwtPayload(username, orgid); + } catch (Exception e) { + return null; + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java new file mode 100644 index 00000000..cccabf46 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java @@ -0,0 +1,474 @@ +package com.link.api.util; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.*; + +public class PaginationUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * SQL Server 2008 通用分页查询(支持 where 条件) + * + * @param orgCode 机构编码 + * @param tableName 表名 + * @param pkColumn 主键字段名(用于排序) + * @param page 当前页码(从 1 开始) + * @param size 每页条数 + * @param whereClause 传入的条件语句(如 "status = 1 AND name LIKE '%张%'"; 可为空) + * @return JSON 字符串(包含分页记录和总数) + */ + public static String paginateSqlServer2008( + String orgCode, + String tableName, + String pkColumn, + int page, + int size, + String whereClause + ) throws SQLException, JsonProcessingException { + + int startRow = (page - 1) * size + 1; + int endRow = page * size; + + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " WHERE " + whereClause + : ""; + + // 总数查询 SQL + String countSql = "SELECT COUNT(*) FROM " + tableName + where; + + // 分页数据 SQL + String dataSql = + "SELECT * FROM (" + + " SELECT ROW_NUMBER() OVER (ORDER BY " + pkColumn + ") AS row_num, * " + + " FROM " + tableName + where + + ") AS t WHERE row_num BETWEEN ? AND ?"; + System.out.println(dataSql); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement countStmt = conn.prepareStatement(countSql); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + // 设置分页参数 + dataStmt.setInt(1, startRow); + dataStmt.setInt(2, endRow); + + // 执行总数查询 + int total = 0; + try (ResultSet rs = countStmt.executeQuery()) { + if (rs.next()) { + total = rs.getInt(1); + } + } + + // 执行数据查询 + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + + + public static String detailSqlServer2008( + String orgCode, + String tableName, + String pkColumn, + String whereClause + ) throws SQLException, JsonProcessingException { + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " WHERE " + whereClause + : ""; + // 分页数据 SQL + String dataSql = "SELECT * FROM " + tableName + where; + System.out.println("dataSql=--==="+dataSql); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("records", records); + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdataSqlServer2008(String orgCode, String tableName,String query) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名"); + } + + // ✅ 拼接安全 SQL,表名拼接,其余参数使用占位符 +// String sql = String.format( +// "SELECT TOP 10 b_id, b_name FROM %s WHERE (b_id LIKE ? OR b_name LIKE ? ) and b_type like ? ORDER BY b_name", +// tableName +// ); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT TOP 10 b_id, b_name FROM ") + .append(tableName) + .append(" WHERE (b_id LIKE ? OR b_name LIKE ?) "); + + sql.append("ORDER BY b_name"); + + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + stmt.setString(2, likeQuery); + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("客户搜索成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdatabyfieldnameSqlServer2008(String orgCode, String tableName, String fieldName, String query) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:表名和字段名只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$") || !fieldName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + String sql=""; + + if(tableName.equals("b_vessel")){ + sql = String.format( + "SELECT TOP 10 b_id, %s ,b_voy FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else if(tableName.equals("b_money")){ + sql = String.format( + "SELECT TOP 10 b_id, %s,b_hl FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else if(tableName.equals("v_cw_account")){ + sql = String.format( + "SELECT TOP 10 b_id, %s,b_account FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else{ + // ✅ 动态生成安全SQL(使用参数化查询) + sql = String.format( + "SELECT TOP 10 b_id, %s FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + } + + + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + // ✅ 参数化查询防止SQL注入 + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdatabyidSqlServer2008( + String orgCode, String tableName, String fieldName, String query + ) throws SQLException, JsonProcessingException { + + // ✅ 校验表名和字段名合法性 + if (!tableName.matches("^[a-zA-Z0-9_]+$") || !fieldName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT TOP 10 b_id, ") + .append(fieldName ) + .append(" FROM " ) +// .append(" as b_name FROM ") + .append(tableName); + + + sql.append(" WHERE b_id LIKE ? OR ") + .append(fieldName) + .append(" LIKE ?"); + + + sql.append(" ORDER BY ").append(fieldName); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + stmt.setString(2, likeQuery); + + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + + private static List> resultSetToList(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + ResultSetMetaData meta = rs.getMetaData(); + int cols = meta.getColumnCount(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= cols; i++) { + String colName = meta.getColumnLabel(i); + Object val = rs.getObject(i); + if (val instanceof Timestamp || val instanceof Date) { + val = dateFormat.format(val); + } + + row.put(colName, val); + } + list.add(row); + } + + return list; + } + + public static String getbasicdatabyselectquerySqlServer2008(String orgCode, String tableName, String query, String orderBy) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:表名和字段名只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$") ) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + // ✅ 动态生成安全SQL(使用参数化查询)"SELECT TOP 10 b_id, b_name FROM %s WHERE b_id LIKE ? OR b_name LIKE ? ORDER BY b_name", + String sql = String.format( + "SELECT col_FieldName as b_id, col_Caption as b_name FROM %s WHERE %s ORDER BY %s", + tableName, // 表名(已校验) + query, + orderBy + ); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + public static String getDataBySql(String orgCode, String sql) throws JsonProcessingException { + // 简单安全检查,只允许 SELECT + if (sql == null || !sql.trim().toUpperCase().startsWith("SELECT")) { + return mapper.writeValueAsString(Result.fail("只允许执行 SELECT 查询语句")); + } + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String paginateSelectSqlServer2008( + String orgCode, + String selectid, + String pkColumn, + int page, + int size, + String whereClause, + String[] psparams + ) throws SQLException, JsonProcessingException { + + int startRow = (page - 1) * size + 1; + int endRow = page * size; + + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " and " + whereClause + : ""; + + String sql = "select * from s_selectsql where selectid = ?"; + + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setString(1, selectid); + + String selectsql=""; + String orderfield = ""; + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + selectsql = rs.getString("selectsql"); + + // 1. 把 :xxx 替换成 ? 占位符 + // 例如: "SELECT * FROM customer WHERE id=:id AND name=:name" + // 替换后: "SELECT * FROM customer WHERE id=? AND name=?" + selectsql = selectsql.replaceAll(":[a-zA-Z0-9_]+", "?"); + + // 用参数数组逐个替换 ? 占位符 + for (int i = 0; i < psparams.length; i++) { + String value = (psparams[i] == null) ? "NULL" : "'" + psparams[i] + "'"; + selectsql = selectsql.replaceFirst("\\?", value); + } + orderfield = rs.getString("orderfield"); + + System.out.println("最终SQL: " + selectsql); + } + } + + if (orderfield != null && !orderfield.trim().isEmpty()) { + // orderfield 不为空,可以使用 + pkColumn = orderfield; + } + + + // 总数查询 SQL + String countSql = "select count(*) from (" + selectsql + where + ") a"; + + // 分页数据 SQL + String dataSql = + "SELECT * FROM (" + + " SELECT ROW_NUMBER() OVER (ORDER BY " + pkColumn + ") AS row_num, t.* " + + " FROM (" + selectsql + " " + where + ") t " + + ") AS temp " + + "WHERE row_num BETWEEN ? AND ?"; + System.out.println(dataSql); + + try ( + PreparedStatement countStmt = conn.prepareStatement(countSql); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + // 设置分页参数 + dataStmt.setInt(1, startRow); + dataStmt.setInt(2, endRow); + + // 执行总数查询 + int total = 0; + try (ResultSet rs = countStmt.executeQuery()) { + if (rs.next()) { + total = rs.getInt(1); + } + } + + // 执行数据查询 + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java new file mode 100644 index 00000000..642a1132 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java @@ -0,0 +1,77 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; + +public class QueryByPkUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * 根据主键查询单条记录 + * + * @param orgCode 机构编码 + * @param tableName 表名 + * @param pkColumn 主键字段名 + * @param pkColumnValue 主键值 + * @return JSON字符串(包含查询到的记录) + */ + public static String queryByPrimaryKey( + String orgCode, + String tableName, + String pkColumn, + String pkColumnValue + ) throws SQLException, JsonProcessingException { + + // 构造查询SQL + String sql = "SELECT * FROM " + tableName + " WHERE " + pkColumn + " = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + // 设置主键参数 + stmt.setString(1, pkColumnValue); + + // 执行查询 + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + Map record = resultSetToMap(rs); + return mapper.writeValueAsString(Result.ok("查询成功", record)); + } else { + return mapper.writeValueAsString(Result.fail("未找到对应记录")); + } + } + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("查询失败:" + e.getMessage())); + } + } + + /** + * 将ResultSet转换为Map + */ + private static Map resultSetToMap(ResultSet rs) throws SQLException { + Map map = new HashMap<>(); + ResultSetMetaData meta = rs.getMetaData(); + int cols = meta.getColumnCount(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + for (int i = 1; i <= cols; i++) { + String colName = meta.getColumnLabel(i); + Object val = rs.getObject(i); + // 格式化日期类型字段 + if (val instanceof Timestamp || val instanceof Date) { + val = dateFormat.format(val); + } + + map.put(colName, val); + } + + return map; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java new file mode 100644 index 00000000..ac4237f7 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java @@ -0,0 +1,66 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class QueryTreeUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + + public static String getTreeNodes( + String orgCode, + String tableName, + String id, + int js + ) throws SQLException, JsonProcessingException { + + int js_new=js; + StringBuilder sql = new StringBuilder("SELECT * FROM " + tableName + " WHERE "); + + sql.append("js = ?"); + + if (id != null && !id.trim().isEmpty()) { + sql.append(" AND b_id LIKE ?"); // 使用预编译参数 + js_new=js_new+1; + } + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + // 设置主键参数 + stmt.setInt(1, js_new); // js参数 + + if (id != null && !id.trim().isEmpty()) { + stmt.setString(2, id + "_%"); + } + + // 执行查询 + ResultSet rs = stmt.executeQuery(); + + // 这里将结果构建为JSON(你原来抛出了 JsonProcessingException) + List> resultList = new ArrayList<>(); + ResultSetMetaData meta = rs.getMetaData(); + int columnCount = meta.getColumnCount(); + + while (rs.next()) { + Map row = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(meta.getColumnLabel(i), rs.getObject(i)); + } + resultList.add(row); + } + + return mapper.writeValueAsString(Result.ok("查询成功", resultList)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("查询失败:" + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/Result.java b/code/link/link-backend/src/main/java/com/link/api/util/Result.java new file mode 100644 index 00000000..fdfc61df --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/Result.java @@ -0,0 +1,40 @@ +package com.link.api.util; + +public class Result { + private boolean success; + private String message; + private T data; + + public Result() {} + + public Result(boolean success, String message) { + this.success = success; + this.message = message; + } + + public Result(boolean success, String message, T data) { + this.success = success; + this.message = message; + this.data = data; + } + + public static Result ok(String msg, T data) { + return new Result<>(true, msg, data); + } + + public static Result ok(String msg) { + return new Result<>(true, msg, null); + } + + public static Result fail(String msg) { + return new Result<>(false, msg, null); + } + + // Getters and setters omitted for brevity + public boolean isSuccess() { return success; } + public void setSuccess(boolean success) { this.success = success; } + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + public T getData() { return data; } + public void setData(T data) { this.data = data; } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java b/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java new file mode 100644 index 00000000..1877ba55 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java @@ -0,0 +1,176 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Date; + +public class TableDataService { + private static final ObjectMapper mapper = new ObjectMapper(); + /** + * 获取表中不允许为空的字段(col_AllowNull="0") + * @param orgCode 组织编码 + * @param tableName 表名 + * @return 包含不允许为空字段的JSON + */ + public static String getMustFields(String orgCode, String tableName) throws JsonProcessingException { + try { + // 1. 获取不允许为空的字段配置 + List notNullFields = getNotNullFieldNames(orgCode, tableName); + if (notNullFields.isEmpty()) { + return toJson(Result.ok("该表所有字段都允许为空", Collections.emptyMap())); + } + + // 2. 构建字段名 -> "0" 的映射 + Map result = new LinkedHashMap<>(); + for (String field : notNullFields) { + result.put(field, "0"); + } + + return toJson(Result.ok("不允许为空的字段列表", result)); + } catch (SQLException e) { + return toJson(Result.fail("数据库错误: " + e.getMessage())); + } catch (JsonProcessingException e) { + return toJson(Result.fail("JSON生成失败")); + } + } + + /** + * 查询不允许为空的字段名列表 + */ + private static List getNotNullFieldNames(String orgCode, String tableName) throws SQLException { + List fieldNames = new ArrayList<>(); + String sql = "SELECT col_FieldName FROM s_columnLib WHERE col_TableName = ? AND col_AllowNull = '0'"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, tableName); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + fieldNames.add(rs.getString("col_FieldName")); + } + } + return fieldNames; + } + + /** + * 根据表名生成带默认值的JSON数据 + * @param orgCode 组织编码(用于获取数据源) + * @param tableName 需要生成数据的表名 + * @return 包含Result结果的JSON字符串 + * + */ + + public static String generateTableDataWithDefaults(String orgCode, String tableName) throws JsonProcessingException { + try { + // 1. 获取字段配置 + List configs = getFieldConfigs(orgCode, tableName); + if (configs.isEmpty()) { + return toJson(Result.fail("未找到表【" + tableName + "】的字段配置")); + } + + // 2. 构建数据 + Map data = buildDefaultData(configs); + + // 3. 返回成功结果 + return toJson(Result.ok("默认数据生成成功", data)); + } catch (SQLException e) { + return toJson(Result.fail("数据库操作失败: " + e.getMessage())); + } catch (JsonProcessingException e) { + return toJson(Result.fail("JSON序列化失败: " + e.getMessage())); + } catch (Exception e) { + return toJson(Result.fail("系统异常: " + e.getMessage())); + } + } + + /** + * 从s_columnLib获取字段配置 + */ + private static List getFieldConfigs(String orgCode, String tableName) throws SQLException { + List configs = new ArrayList<>(); + String sql = "SELECT col_FieldName, col_DefaultValue FROM s_columnLib WHERE col_TableName = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, tableName); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + configs.add(new FieldConfig( + rs.getString("col_FieldName"), + rs.getString("col_DefaultValue") + )); + } + } + } + return configs; + } + + /** + * 构建默认数据映射 + */ + private static Map buildDefaultData(List configs) { + Map data = new LinkedHashMap<>(); + for (FieldConfig config : configs) { + data.put(config.fieldName, parseDefaultValue(config.defaultValue)); + } + return data; + } + + /** + * 智能解析默认值 + */ + private static Object parseDefaultValue(String value) { + if (value == null || value.trim().isEmpty()) { + return ""; + } + + // 特殊关键字处理 + switch (value.toLowerCase()) { + case "date": + // 返回当前日期(格式:yyyy-MM-dd) + return new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + case "user": + // 返回 user+1(示例逻辑,可根据需求调整) + return UserContext.getUserId(); + case "now": + // 返回当前日期(格式:yyyy-MM-dd) + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } + + // 布尔值处理 + if ("true".equalsIgnoreCase(value)) return true; + if ("false".equalsIgnoreCase(value)) return false; + + // 数字类型处理 + try { + return value.contains(".") + ? Double.parseDouble(value) + : Long.parseLong(value); + } catch (NumberFormatException ignored) { + return value; + } + } + + /** + * 对象转JSON(统一处理异常) + */ + private static String toJson(Object obj) throws JsonProcessingException { + return mapper.writeValueAsString(obj); + } + + // 字段配置内部类 + private static class FieldConfig { + final String fieldName; + final String defaultValue; + + FieldConfig(String fieldName, String defaultValue) { + this.fieldName = fieldName; + this.defaultValue = defaultValue; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/Tools.java b/code/link/link-backend/src/main/java/com/link/api/util/Tools.java new file mode 100644 index 00000000..eb898a4b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/Tools.java @@ -0,0 +1,121 @@ +package com.link.api.util; + +import java.util.HashMap; +import java.util.Map; + + +public final class Tools { + + // ================== Snowflake 单例模式 ================== + private static volatile Snowflake INSTANCE; + + /** + * 获取 Snowflake 单例(默认 workerId=1, datacenterId=1) + */ + public static Snowflake snowflake() { + if (INSTANCE == null) { + synchronized (Tools.class) { + if (INSTANCE == null) { + INSTANCE = new Snowflake(1, 1); + } + } + } + return INSTANCE; + } + + /** + * 快速生成唯一ID(使用默认单例) + */ + public static long nextId() { + return snowflake().nextId(); + } + + // ================== Snowflake 实现 ================== + public static final class Snowflake { + private static final long EPOCH = 1288834974657L; + private static final long WORKER_ID_BITS = 5L; + private static final long DATACENTER_ID_BITS = 5L; + private static final long SEQUENCE_BITS = 12L; + + private final long workerId; + private final long datacenterId; + private long sequence = 0L; + private long lastTimestamp = -1L; + + public Snowflake(long workerId, long datacenterId) { + this.workerId = workerId; + this.datacenterId = datacenterId; + validateIds(); + } + + public synchronized long nextId() { + long timestamp = System.currentTimeMillis(); + if (timestamp < lastTimestamp) { + throw new RuntimeException("时钟回拨!拒绝生成ID"); + } + if (timestamp == lastTimestamp) { + sequence = (sequence + 1) & ((1 << SEQUENCE_BITS) - 1); + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + lastTimestamp = timestamp; + return ((timestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS)) + | (datacenterId << (WORKER_ID_BITS + SEQUENCE_BITS)) + | (workerId << SEQUENCE_BITS) + | sequence; + } + + private void validateIds() { + long maxWorkerId = ~(-1L << WORKER_ID_BITS); + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException("Worker ID 必须介于 0 和 " + maxWorkerId + " 之间"); + } + long maxDatacenterId = ~(-1L << DATACENTER_ID_BITS); + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException("Datacenter ID 必须介于 0 和 " + maxDatacenterId + " 之间"); + } + } + + private long tilNextMillis(long lastTimestamp) { + long timestamp = System.currentTimeMillis(); + while (timestamp <= lastTimestamp) { + timestamp = System.currentTimeMillis(); + } + return timestamp; + } + } + + // ================== Map 工具方法 ================== + public static Map mapOf(Object... kvs) { + if (kvs.length % 2 != 0) { + throw new IllegalArgumentException("参数必须是 key-value 对"); + } + Map map = new HashMap<>(); + for (int i = 0; i < kvs.length; i += 2) { + map.put(String.valueOf(kvs[i]), kvs[i + 1]); + } + return map; + } + + // 禁止实例化 + private Tools() {} +} + +//public class tools { +// +// public static Map mapOf(Object... kvs) { +// if (kvs.length % 2 != 0) { +// throw new IllegalArgumentException("参数个数必须为偶数,代表 key-value 对"); +// } +// +// Map map = new HashMap<>(); +// for (int i = 0; i < kvs.length; i += 2) { +// map.put(String.valueOf(kvs[i]), kvs[i + 1]); +// } +// return map; +// } +// +//} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java b/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java new file mode 100644 index 00000000..4dc65d67 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java @@ -0,0 +1,47 @@ +package com.link.api.util; + + +/** + * 用户上下文,用于存储每个请求线程的用户信息,如 userId 和 orgId(租户ID)。 + */ +public class UserContext { + + // 使用 ThreadLocal 保存当前线程的用户上下文 + private static final ThreadLocal userContextThreadLocal = new ThreadLocal<>(); + + private final String userId; + private final String orgId; // orgId 表示租户标识 + + private UserContext(String userId, String orgId) { + this.userId = userId; + this.orgId = orgId; + } + + /** 设置当前线程用户上下文 */ + public static void set(String userId, String orgId) { + userContextThreadLocal.set(new UserContext(userId, orgId)); + } + + /** 获取完整的 UserContext 对象 */ + public static UserContext get() { + return userContextThreadLocal.get(); + } + + /** 获取当前用户 ID(静态便捷方法) */ + public static String getUserId() { + UserContext context = get(); + return context != null ? context.userId : null; + } + + /** 获取当前 orgId(租户 ID) */ + public static String getOrgId() { + UserContext context = get(); + return context != null ? context.orgId : null; + } + + /** 清除上下文(防止内存泄露) */ + public static void clear() { + userContextThreadLocal.remove(); + } +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java deleted file mode 100644 index 5c82a493..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.link.api.utils; - -import com.link.api.model.ExceptionEntity; -import com.link.api.exception.BaseException; - -import javax.servlet.http.HttpServletRequest; -import java.util.Date; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -public class ExceptionUtils { - - private static final ObjectMapper objectMapper = new ObjectMapper(); - - /** - * 根据异常和请求信息,生成统一的异常信息对象 ExceptionDetailInfo - * @param ex 异常对象 - * @param request HttpServletRequest,用来获取请求路径和方法等 - * @param projectBasePackage 项目基础包名,如 "com.error",用来定位业务代码异常栈帧 - * @return ExceptionDetailInfo 封装的异常信息 - */ - public static ExceptionEntity extractExceptionInfo(Exception ex, HttpServletRequest request, String projectBasePackage) { - StackTraceElement[] stackTrace = ex.getStackTrace(); - - StackTraceElement source = null; - if (stackTrace != null) { - for (StackTraceElement element : stackTrace) { - if (element.getClassName().startsWith(projectBasePackage)) { - source = element; - break; - } - } - if (source == null && stackTrace.length > 0) { - source = stackTrace[0]; - } - } - - String briefMessage; - - if (ex instanceof BaseException) { - BaseException be = (BaseException) ex; - briefMessage = be.getMessage(); - } else { - if (ex.getMessage() != null) { - String[] lines = ex.getMessage().split("\n"); - // 遍历每一行,找到第一行“非空、非符号”的有效信息 - String usefulLine = null; - for (String line : lines) { - String trimmed = line.trim(); - if (!trimmed.isEmpty()) { - usefulLine = trimmed; - break; - } - } - // 如果找到有效行,就用它;否则仅用异常类名 - if (usefulLine != null) { - briefMessage = ex.getClass().getSimpleName() + ": " + usefulLine; - } else { - briefMessage = ex.getClass().getSimpleName(); - } - } else { - briefMessage = ex.getClass().getSimpleName(); - } - } - - String paramJson = null; - if (request != null) { - Map paramMap = new HashMap<>(); - Enumeration paramNames = request.getParameterNames(); - while (paramNames.hasMoreElements()) { - String paramName = paramNames.nextElement(); - paramMap.put(paramName, request.getParameter(paramName)); - } - try { - paramJson = JsonUtils.toJson(paramMap); - } catch (Exception e) { - paramJson = "参数序列化失败: " + e.getMessage(); - } - } - - return ExceptionEntity.builder() - .id(SnowflakeIdUtils.nextId()) - .uri(request != null ? request.getRequestURI() : null) - .httpMethod(request != null ? request.getMethod() : null) - .className(source != null ? source.getClassName() : null) - .methodName(source != null ? source.getMethodName() : null) - .fileName(source != null ? source.getFileName() : null) - .lineNumber(source != null ? source.getLineNumber() : null) - .summary(briefMessage) - .detailMessage(org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(ex)) - .time(new Date()) - .params(paramJson) - .build(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java deleted file mode 100644 index c04bc999..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.link.api.utils; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class JsonUtils { - private static final ObjectMapper objectMapper = new ObjectMapper(); - // 判断字符串是否为有效的 JSON 格式 - public static boolean isTypeJSON(String msgText) { - try { - // 尝试将字符串解析为 JsonNode,如果能解析,说明是有效的 JSON - JsonNode node = objectMapper.readTree(msgText); - return node != null; - } catch (Exception e) { - return false; // 如果抛出异常,说明不是有效的 JSON - } - } - - // 将 JSON 字符串转换为指定类型的 Java 对象 - public static T toBean(String json, Class clazz) { - try { - // 使用 ObjectMapper 将 JSON 字符串转换为指定类型的对象 - return objectMapper.readValue(json, clazz); - } catch (JsonProcessingException e) { - e.printStackTrace(); // 打印异常信息 - return null; // 如果转换失败,返回 null - } - } - - // 将对象转换为 JSON 字符串 - public static String toJson(Object obj) { - try { - return objectMapper.writeValueAsString(obj); // 将对象转换为 JSON 字符串 - } catch (JsonProcessingException e) { - System.out.println(e.getMessage()); - return null; // 如果出现异常,返回 null - } - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java deleted file mode 100644 index 81f6c950..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.link.api.utils; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SnowflakeIdUtils { - - // 起始时间戳(建议设置为项目上线时间) - private static final long START_TIMESTAMP = 1288834974657L; - - // 各部分位数 - private static final long SEQUENCE_BITS = 12L; - private static final long MACHINE_BITS = 5L; - private static final long DATACENTER_BITS = 5L; - - // 最大值 - private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); - private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS); - private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_BITS); - - // 左移位数 - private static final long MACHINE_LEFT = SEQUENCE_BITS; - private static final long DATACENTER_LEFT = SEQUENCE_BITS + MACHINE_BITS; - private static final long TIMESTAMP_LEFT = SEQUENCE_BITS + MACHINE_BITS + DATACENTER_BITS; - - // 参数及状态(静态) - private static long datacenterId; - private static long machineId; - private static long sequence = 0L; - private static long lastTimestamp = -1L; - private static boolean initialized = false; - - // 初始化方法(只能调用一次) - public static synchronized void init(long dcId, long mId) { - if (initialized) { - throw new IllegalStateException("SnowflakeIdGenerator has already been initialized."); - } - if (dcId > MAX_DATACENTER_ID || dcId < 0) { - throw new IllegalArgumentException("datacenterId must be between 0 and " + MAX_DATACENTER_ID); - } - if (mId > MAX_MACHINE_ID || mId < 0) { - throw new IllegalArgumentException("machineId must be between 0 and " + MAX_MACHINE_ID); - } - datacenterId = dcId; - machineId = mId; - initialized = true; - } - - // 生成 ID(线程安全) - public static synchronized long nextId() { - if (!initialized) { - throw new IllegalStateException("SnowflakeIdGenerator not initialized. Call init() first."); - } - - long currentTimestamp = timeGen(); - - if (currentTimestamp < lastTimestamp) { - throw new RuntimeException("Clock moved backwards. Refusing to generate id."); - } - - if (currentTimestamp == lastTimestamp) { - sequence = (sequence + 1) & MAX_SEQUENCE; - if (sequence == 0) { - currentTimestamp = waitNextMillis(currentTimestamp); - } - } else { - sequence = 0L; - } - - lastTimestamp = currentTimestamp; - - return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT) - | (datacenterId << DATACENTER_LEFT) - | (machineId << MACHINE_LEFT) - | sequence; - } - - private static long waitNextMillis(long currentTimestamp) { - while (currentTimestamp <= lastTimestamp) { - currentTimestamp = timeGen(); - } - return currentTimestamp; - } - - private static long timeGen() { - return System.currentTimeMillis(); - } -} - diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java deleted file mode 100644 index 0faca58b..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.link.api.utils; - -import org.springframework.aop.framework.AopContext; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -/** - * spring工具类 方便在非spring管理环境中获取bean - * - * @author webserver - */ -@Component -public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware -{ - /** Spring应用上下文环境 */ - private static ConfigurableListableBeanFactory beanFactory; - - private static ApplicationContext applicationContext; - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException - { - SpringUtils.beanFactory = beanFactory; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - SpringUtils.applicationContext = applicationContext; - } - - /** - * 获取对象 - * - * @param name - * @return Object 一个以所给名字注册的bean的实例 - * @throws org.springframework.beans.BeansException - * - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) throws BeansException - { - return (T) beanFactory.getBean(name); - } - - /** - * 获取类型为requiredType的对象 - * - * @param clz - * @return - * @throws org.springframework.beans.BeansException - * - */ - public static T getBean(Class clz) throws BeansException - { - T result = (T) beanFactory.getBean(clz); - return result; - } - - /** - * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true - * - * @param name - * @return boolean - */ - public static boolean containsBean(String name) - { - return beanFactory.containsBean(name); - } - - /** - * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) - * - * @param name - * @return boolean - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.isSingleton(name); - } - - /** - * @param name - * @return Class 注册对象的类型 - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static Class getType(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getType(name); - } - - /** - * 如果给定的bean名字在bean定义中有别名,则返回这些别名 - * - * @param name - * @return - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static String[] getAliases(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getAliases(name); - } - - /** - * 获取aop代理对象 - * - * @param invoker - * @return - */ - @SuppressWarnings("unchecked") - public static T getAopProxy(T invoker) - { - return (T) AopContext.currentProxy(); - } - - /** - * 获取当前的环境配置,无配置返回null - * - * @return 当前的环境配置 - */ - public static String[] getActiveProfiles() - { - return applicationContext.getEnvironment().getActiveProfiles(); - } - - /** - * 获取当前的环境配置,当有多个环境配置时,只获取第一个 - * - * @return 当前的环境配置 - */ - public static String getActiveProfile() - { - final String[] activeProfiles = getActiveProfiles(); - return activeProfiles.length > 0 ? activeProfiles[0] : null; - } - - /** - * 获取配置文件中的值 - * - * @param key 配置文件的key - * @return 当前的配置文件的值 - * - */ - public static String getRequiredProperty(String key) - { - return applicationContext.getEnvironment().getRequiredProperty(key); - } -} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/im/ImServer.java b/code/link/link-backend/src/main/java/com/link/im/ImServer.java index 2b95852d..bd9aede3 100644 --- a/code/link/link-backend/src/main/java/com/link/im/ImServer.java +++ b/code/link/link-backend/src/main/java/com/link/im/ImServer.java @@ -1,5 +1,6 @@ package com.link.im; +import com.link.im.handler.ImWebSocketHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; diff --git a/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java b/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java deleted file mode 100644 index 6324f833..00000000 --- a/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.link.im; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.netty.channel.*; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.timeout.IdleStateEvent; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -@ChannelHandler.Sharable -public class ImWebSocketHandler extends SimpleChannelInboundHandler { - - private static final ConcurrentHashMap CLIENTS = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap USER_CHANNEL_MAP = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap> GROUPS = new ConcurrentHashMap<>(); - private static final ObjectMapper MAPPER = new ObjectMapper(); - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - String channelId = ctx.channel().id().asShortText(); - CLIENTS.put(channelId, ctx.channel()); - - System.out.println("✅ 客户端连接: " + channelId); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) { - String channelId = ctx.channel().id().asShortText(); - CLIENTS.remove(channelId); - - USER_CHANNEL_MAP.entrySet().removeIf(entry -> entry.getValue().equals(channelId)); - GROUPS.values().forEach(set -> set.remove(channelId)); - - System.out.println("❌ 客户端断开: " + channelId); - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { - String text = msg.text(); - System.out.println("📩 收到消息: " + text); - - try { - JsonNode json = MAPPER.readTree(text); - String type = json.path("type").asText(); - - switch (type) { - case "bind": { // 绑定用户ID到连接 - String userId = json.path("userId").asText(); - USER_CHANNEL_MAP.put(userId, ctx.channel().id().asShortText()); - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"system\",\"msg\":\"绑定成功\"}")); - break; - } - case "ping": { // 心跳 - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"pong\"}")); - break; - } - case "single": { // 单聊 - String to = json.path("to").asText(); - String content = json.path("content").asText(); - String from = json.path("from").asText(); - - sendToUser(to, String.format("{\"type\":\"single\",\"from\":\"%s\",\"content\":\"%s\"}", from, content)); - break; - } - case "group": { // 群聊 - String groupId = json.path("groupId").asText(); - String from = json.path("from").asText(); - String content = json.path("content").asText(); - - broadcastGroup(groupId, String.format("{\"type\":\"group\",\"from\":\"%s\",\"content\":\"%s\"}", from, content)); - break; - } - default: - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"error\",\"msg\":\"未知消息类型\"}")); - } - } catch (Exception e) { - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"error\",\"msg\":\"JSON格式错误\"}")); - } - } - - private void sendToUser(String userId, String message) { - String channelId = USER_CHANNEL_MAP.get(userId); - if (channelId != null) { - Channel ch = CLIENTS.get(channelId); - if (ch != null && ch.isActive()) { - ch.writeAndFlush(new TextWebSocketFrame(message)); - } - } - } - - private void broadcastGroup(String groupId, String message) { - Set members = GROUPS.get(groupId); - if (members == null) return; - for (String channelId : members) { - Channel ch = CLIENTS.get(channelId); - if (ch != null && ch.isActive()) { - ch.writeAndFlush(new TextWebSocketFrame(message)); - } - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - // 如果仍保留 IdleStateHandler,可以检测心跳超时 - if (evt instanceof IdleStateEvent) { - System.out.println("💤 连接超时,关闭: " + ctx.channel().id().asShortText()); - ctx.close(); - } else { - super.userEventTriggered(ctx, evt); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java b/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java new file mode 100644 index 00000000..3f806f8f --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java @@ -0,0 +1,70 @@ +package com.link.im.handler; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.link.im.manager.GroupManager; +import com.link.im.manager.UserChannelManager; +import com.link.im.manager.WriteManager; +import com.link.im.model.ChatMessage; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.timeout.IdleStateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@ChannelHandler.Sharable +public class ImWebSocketHandler extends SimpleChannelInboundHandler { + private static final Logger logger = LoggerFactory.getLogger(ImWebSocketHandler.class); + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Override + public void handlerAdded(ChannelHandlerContext ctx) { + System.out.println("✅ 客户端连接: " + ctx.channel().id().asShortText()); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + // 下线时通过 UserChannelManager 移除 + UserChannelManager.removeUserByChannel(ctx.channel()); + + // 同时从所有群中移除 + GroupManager.removeUserFromAllGroups(ctx.channel()); + + System.out.println("❌ 客户端断开: " + ctx.channel().id().asShortText()); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + String text = msg.text(); + try { + ChatMessage chatMsg = MAPPER.readValue(text, ChatMessage.class); + + System.out.println(chatMsg); + } catch (Exception e) { + WriteManager.writeToChannel(ctx.channel(),"JSON格式错误"); + } + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + System.out.println("💤 连接超时,关闭: " + ctx.channel().id().asShortText()); + ctx.close(); + } else { + super.userEventTriggered(ctx, evt); + } + } + + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + logger.error("Channel 异常, 关闭连接: {}", ctx.channel().id().asShortText(), cause); + ctx.close(); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java new file mode 100644 index 00000000..1bcea325 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java @@ -0,0 +1,71 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class GroupManager { + private static final Logger logger = LoggerFactory.getLogger(GroupManager.class); + + // groupId -> Set + private static final ConcurrentHashMap> GROUP_MAP = new ConcurrentHashMap<>(); + + /** + * 创建或获取群成员集合 + */ + private static Set getGroup(String groupId) { + return GROUP_MAP.computeIfAbsent(groupId, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())); + } + + /** + * 添加成员到群 + */ + public static void addUser(String groupId, String userId) { + if (groupId == null || userId == null) return; + getGroup(groupId).add(userId); + } + + /** + * 从群中移除成员 + */ + public static void removeUser(String groupId, String userId) { + Set members = GROUP_MAP.get(groupId); + if (members != null) { + members.remove(userId); + } + } + + /** + * 根据 Channel 移除该用户在所有群的成员记录 + */ + public static void removeUserFromAllGroups(Channel channel) { + if (channel == null) return; + + // 遍历所有群 + GROUP_MAP.forEach((groupId, members) -> { + members.removeIf(userId -> { + Channel ch = UserChannelManager.getChannel(userId); + boolean match = ch != null && ch.equals(channel); + if (match) { + logger.info("用户从群移除: groupId={}, userId={}", groupId, userId); + } + return match; + }); + }); + } + + /** + * 获取群成员列表(不可修改) + */ + public static Set getMembers(String groupId) { + Set members = GROUP_MAP.get(groupId); + if (members == null) return Collections.emptySet(); + return Collections.unmodifiableSet(members); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java new file mode 100644 index 00000000..6c4b4dca --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java @@ -0,0 +1,64 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ConcurrentHashMap; + +public class UserChannelManager { + private static final Logger logger = LoggerFactory.getLogger(UserChannelManager.class); + + private static final ConcurrentHashMap userChannelMap = new ConcurrentHashMap<>(); + + /** + * 添加用户与Channel绑定 + */ + public static void addUser(String userId, Channel channel) { + if (userId != null && channel != null) { + userChannelMap.put(userId, channel); + } + } + + /** + * 移除用户绑定 + */ + public static void removeUserById(String userId) { + if (userId != null) { + userChannelMap.remove(userId); + } + } + + /** + * 根据 Channel 移除对应用户绑定 + */ + public static void removeUserByChannel(Channel channel) { + if (channel == null) return; + + userChannelMap.entrySet().removeIf(entry -> { + boolean match = entry.getValue().equals(channel); + if (match) { + logger.info("用户下线: userId={}, channelId={}", entry.getKey(), channel.id().asShortText()); + } + return match; + }); + } + + /** + * 根据 userId 获取 Channel + */ + public static Channel getChannel(String userId) { + return userChannelMap.get(userId); + } + + /** + * 判断用户是否在线 + */ + public static boolean isOnline(String userId) { + Channel ch = userChannelMap.get(userId); + return ch != null && ch.isActive(); + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java new file mode 100644 index 00000000..742126da --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java @@ -0,0 +1,57 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.link.im.handler.ImWebSocketHandler; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public class WriteManager { + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final Logger logger = LoggerFactory.getLogger(WriteManager.class); + + /** + * 发送消息 - 根据 userId + */ + public static void writeToUser(String userId, Object message) { + Channel ch = UserChannelManager.getChannel(userId); + writeToChannel(ch, message); + } + + /** + * 发送消息 - 根据 Channel + */ + public static void writeToChannel(Channel channel, Object message) { + if (channel != null && channel.isActive() && message != null) { + try { + String json = message instanceof String ? (String) message : MAPPER.writeValueAsString(message); + channel.writeAndFlush(new TextWebSocketFrame(json)); + } catch (Exception e) { + logger.error("发送消息失败", e); + } + } + } + + /** + * 向群内所有在线成员广播消息 + * 只在异常时记录日志 + */ + public static void writeToGroup(String groupId, Object message) { + Set members = GroupManager.getMembers(groupId); + for (String userId : members) { + try { + Channel ch = UserChannelManager.getChannel(userId); + if (ch != null && ch.isActive() && message != null) { + String json = message instanceof String ? (String) message : MAPPER.writeValueAsString(message); + + ch.writeAndFlush(new TextWebSocketFrame(json)); + } + } catch (Exception e) { + logger.error("❌ 群消息发送失败 groupId={} userId={}", groupId, userId, e); + } + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java b/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java new file mode 100644 index 00000000..32a2e9a4 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java @@ -0,0 +1,40 @@ +package com.link.im.model; + +import java.util.List; + +public class ChatMessage { + private String type; + private String msg; + + public ChatMessage() { + } + + public ChatMessage(String type, String msg) { + this.type = type; + this.msg = msg; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public String toString() { + return "ChatMessage{" + + "type='" + type + '\'' + + ", msg='" + msg + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/application.properties b/code/link/link-backend/src/main/resources/application.properties new file mode 100644 index 00000000..8fa00146 --- /dev/null +++ b/code/link/link-backend/src/main/resources/application.properties @@ -0,0 +1,17 @@ +#spring.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=G3HD +server.port=8089 +# Netty WebSocket????? +netty.port=8090 +spring.datasource.url=jdbc:p6spy:sqlserver://127.0.0.1:1433;DatabaseName=G3HY2025 +spring.datasource.username=sa +spring.datasource.password=g3soft +#spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver +spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver +spring.datasource.hikari.maximum-pool-size=10 +logging.level.com.zaxxer.hikari=DEBUG +logging.level.java.sql=DEBUG +logging.level.org.springframework.jdbc.core=DEBUG +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG +logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE +logging.level.p6spy=DEBUG +logging.level.com.p6spy=DEBUG diff --git a/code/link/link-backend/src/main/resources/application.yml b/code/link/link-backend/src/main/resources/application.yml deleted file mode 100644 index 2d55dabd..00000000 --- a/code/link/link-backend/src/main/resources/application.yml +++ /dev/null @@ -1,25 +0,0 @@ -server: - servlet: - context-path: / # 应用的访问路径 - tomcat: - uri-encoding: UTF-8 # URI编码 - accept-count: 1000 # 连接数满后的排队数 - threads: - max: 800 # 最大线程数 - min-spare: 100 # 初始化的线程数 - port: 8089 - -netty: - port: 8090 - -spring: - config: - name: application - location: classpath:/,file:./config/ - jackson: - date-format: yyyy-MM-dd HH:mm:ss - time-zone: Asia/Shanghai - servlet: - multipart: - max-file-size: -1 - max-request-size: -1 diff --git a/code/link/link-backend/src/main/resources/backup/application.yml b/code/link/link-backend/src/main/resources/backup/application.yml new file mode 100644 index 00000000..e18116be --- /dev/null +++ b/code/link/link-backend/src/main/resources/backup/application.yml @@ -0,0 +1,8 @@ +spring: + datasource: + url: jdbc:sqlserver://192.168.1.189:1433;databaseName=G3HD + username: sa + password: g3soft + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +server: + port: 8080 diff --git a/code/link/link-backend/src/main/resources/datasource.yml b/code/link/link-backend/src/main/resources/datasource.yml deleted file mode 100644 index 2b9b0951..00000000 --- a/code/link/link-backend/src/main/resources/datasource.yml +++ /dev/null @@ -1,22 +0,0 @@ -#datasource: -# db1: -# url: jdbc:jtds:sqlserver://127.0.0.1:1433;databaseName=test1 -# username: sa -# password: sa -# driverClassName: net.sourceforge.jtds.jdbc.Driver -# db2: -# url: jdbc:jtds:sqlserver://127.0.0.1:1433;databaseName=test2 -# username: sa -# password: sa -# driverClassName: net.sourceforge.jtds.jdbc.Driver -datasource: - db1: - url: jdbc:jtds:sqlserver://192.168.1.176:1433;databaseName=G3-Email - username: sa - password: sa - driverClassName: net.sourceforge.jtds.jdbc.Driver - db2: - url: jdbc:jtds:sqlserver://192.168.1.176:1433;databaseName=G3-Website - username: sa - password: sa - driverClassName: net.sourceforge.jtds.jdbc.Driver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties b/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties new file mode 100644 index 00000000..7506e981 --- /dev/null +++ b/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HY2025 +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties b/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties new file mode 100644 index 00000000..bb2277ab --- /dev/null +++ b/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HD +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/spy.properties b/code/link/link-backend/src/main/resources/spy.properties new file mode 100644 index 00000000..fc9a6edb --- /dev/null +++ b/code/link/link-backend/src/main/resources/spy.properties @@ -0,0 +1,12 @@ +# ?? slf4j ?????? +appender=com.p6spy.engine.spy.appender.Slf4JLogger + +# ???? +logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat + +# ???? +logLevel=INFO + +# ?????????????SQL?? +excludecategories=info,debug,result,batch + diff --git a/code/link/link-backend/src/main/resources/test.html b/code/link/link-backend/src/main/resources/test.html index 3d7b857a..3b245cb8 100644 --- a/code/link/link-backend/src/main/resources/test.html +++ b/code/link/link-backend/src/main/resources/test.html @@ -12,7 +12,7 @@ .card{background:var(--card);padding:14px;border-radius:10px;box-shadow:0 1px 4px rgba(2,6,23,0.06);margin-bottom:12px} .row{display:flex;gap:8px;align-items:center} input[type="text"], input[type="number"], textarea, select { - padding:8px;border:1px solid #e6e9ef;border-radius:6px;font-size:13px; + padding:8px;border:1px solid #e6e9ef;border-radius:6px;font-size:13px; } button{background:var(--accent);color:#fff;border:0;padding:8px 12px;border-radius:8px;cursor:pointer} button.secondary{background:#e6eefc;color:#08306b} @@ -104,183 +104,183 @@ diff --git a/code/link/link-backend/target/classes/application.properties b/code/link/link-backend/target/classes/application.properties new file mode 100644 index 00000000..8fa00146 --- /dev/null +++ b/code/link/link-backend/target/classes/application.properties @@ -0,0 +1,17 @@ +#spring.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=G3HD +server.port=8089 +# Netty WebSocket????? +netty.port=8090 +spring.datasource.url=jdbc:p6spy:sqlserver://127.0.0.1:1433;DatabaseName=G3HY2025 +spring.datasource.username=sa +spring.datasource.password=g3soft +#spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver +spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver +spring.datasource.hikari.maximum-pool-size=10 +logging.level.com.zaxxer.hikari=DEBUG +logging.level.java.sql=DEBUG +logging.level.org.springframework.jdbc.core=DEBUG +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG +logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE +logging.level.p6spy=DEBUG +logging.level.com.p6spy=DEBUG diff --git a/code/link/link-backend/target/classes/backup/application.yml b/code/link/link-backend/target/classes/backup/application.yml new file mode 100644 index 00000000..e18116be --- /dev/null +++ b/code/link/link-backend/target/classes/backup/application.yml @@ -0,0 +1,8 @@ +spring: + datasource: + url: jdbc:sqlserver://192.168.1.189:1433;databaseName=G3HD + username: sa + password: g3soft + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +server: + port: 8080 diff --git a/code/link/link-backend/target/classes/com/link/LinkApplication.class b/code/link/link-backend/target/classes/com/link/LinkApplication.class new file mode 100644 index 00000000..7a4e4fbb Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/LinkApplication.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/AuthController.class b/code/link/link-backend/target/classes/com/link/api/controller/AuthController.class new file mode 100644 index 00000000..e69a9c6d Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/AuthController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/ColumnConfigController.class b/code/link/link-backend/target/classes/com/link/api/controller/ColumnConfigController.class new file mode 100644 index 00000000..50b962d5 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/ColumnConfigController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataListController.class b/code/link/link-backend/target/classes/com/link/api/controller/DataListController.class new file mode 100644 index 00000000..4ba63f62 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/DataListController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataObjController.class b/code/link/link-backend/target/classes/com/link/api/controller/DataObjController.class new file mode 100644 index 00000000..1588887b Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/DataObjController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController$1.class b/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController$1.class new file mode 100644 index 00000000..311954f7 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController$1.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class b/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class new file mode 100644 index 00000000..60ce3f7c Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class b/code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class new file mode 100644 index 00000000..9ff0220d Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class b/code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class new file mode 100644 index 00000000..ca1468d5 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/StudentController.class b/code/link/link-backend/target/classes/com/link/api/controller/StudentController.class new file mode 100644 index 00000000..8d0b82e7 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/StudentController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/TreeController.class b/code/link/link-backend/target/classes/com/link/api/controller/TreeController.class new file mode 100644 index 00000000..4ac67110 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/TreeController.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/controller/WebConfig.class b/code/link/link-backend/target/classes/com/link/api/controller/WebConfig.class new file mode 100644 index 00000000..219b9665 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/controller/WebConfig.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/manager/UserManager.class b/code/link/link-backend/target/classes/com/link/api/manager/UserManager.class new file mode 100644 index 00000000..d2a597a2 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/manager/UserManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class b/code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class new file mode 100644 index 00000000..4a9e56fb Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/model/Student.class b/code/link/link-backend/target/classes/com/link/api/model/Student.class new file mode 100644 index 00000000..ba2f437a Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/model/Student.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/model/UserInfo.class b/code/link/link-backend/target/classes/com/link/api/model/UserInfo.class new file mode 100644 index 00000000..a5c9d44d Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/model/UserInfo.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/repository/MultiTenantUserDao.class b/code/link/link-backend/target/classes/com/link/api/repository/MultiTenantUserDao.class new file mode 100644 index 00000000..4f9bee13 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/repository/MultiTenantUserDao.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class b/code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class new file mode 100644 index 00000000..32af3a42 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/repository/StudentDaoJDBC.class b/code/link/link-backend/target/classes/com/link/api/repository/StudentDaoJDBC.class new file mode 100644 index 00000000..a5e406f1 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/repository/StudentDaoJDBC.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class b/code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class new file mode 100644 index 00000000..cfe9618b Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class b/code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class new file mode 100644 index 00000000..8a24f852 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class new file mode 100644 index 00000000..eb9d3e29 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class new file mode 100644 index 00000000..577d4e4a Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTable.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTable.class new file mode 100644 index 00000000..b8601971 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTable.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class new file mode 100644 index 00000000..51cb8122 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class new file mode 100644 index 00000000..19115536 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class new file mode 100644 index 00000000..58efdd04 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2bk.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2bk.class new file mode 100644 index 00000000..f5dd0deb Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2bk.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class new file mode 100644 index 00000000..bf2c2dbe Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/BuildSql.class b/code/link/link-backend/target/classes/com/link/api/util/BuildSql.class new file mode 100644 index 00000000..0eb94c70 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/BuildSql.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class b/code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class new file mode 100644 index 00000000..c1393da5 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class b/code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class new file mode 100644 index 00000000..dadc0b70 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/JsonIMUtil.class b/code/link/link-backend/target/classes/com/link/api/util/JsonIMUtil.class new file mode 100644 index 00000000..fe346cee Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/JsonIMUtil.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/JsonUtils.class b/code/link/link-backend/target/classes/com/link/api/util/JsonUtils.class new file mode 100644 index 00000000..9aeee255 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/JsonUtils.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class b/code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class new file mode 100644 index 00000000..bc0ce3b9 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/JwtPayload.class b/code/link/link-backend/target/classes/com/link/api/util/JwtPayload.class new file mode 100644 index 00000000..cf6cf378 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/JwtPayload.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class b/code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class new file mode 100644 index 00000000..400af62b Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class b/code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class new file mode 100644 index 00000000..1ec6d0f6 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class b/code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class new file mode 100644 index 00000000..be409aa8 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/QueryTreeUtils.class b/code/link/link-backend/target/classes/com/link/api/util/QueryTreeUtils.class new file mode 100644 index 00000000..9e5c2c60 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/QueryTreeUtils.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/Result.class b/code/link/link-backend/target/classes/com/link/api/util/Result.class new file mode 100644 index 00000000..89c61bdb Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/Result.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class b/code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class new file mode 100644 index 00000000..141ed55a Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/TableDataService.class b/code/link/link-backend/target/classes/com/link/api/util/TableDataService.class new file mode 100644 index 00000000..66cb9436 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/TableDataService.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class b/code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class new file mode 100644 index 00000000..75a93aaa Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/Tools.class b/code/link/link-backend/target/classes/com/link/api/util/Tools.class new file mode 100644 index 00000000..9d167b6c Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/Tools.class differ diff --git a/code/link/link-backend/target/classes/com/link/api/util/UserContext.class b/code/link/link-backend/target/classes/com/link/api/util/UserContext.class new file mode 100644 index 00000000..9f49c7e8 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/api/util/UserContext.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/ImServer$1.class b/code/link/link-backend/target/classes/com/link/im/ImServer$1.class new file mode 100644 index 00000000..cfba2d3b Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/ImServer$1.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/ImServer.class b/code/link/link-backend/target/classes/com/link/im/ImServer.class new file mode 100644 index 00000000..9a89959b Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/ImServer.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/handler/ImWebSocketHandler.class b/code/link/link-backend/target/classes/com/link/im/handler/ImWebSocketHandler.class new file mode 100644 index 00000000..bee8210a Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/handler/ImWebSocketHandler.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/manager/GroupManager.class b/code/link/link-backend/target/classes/com/link/im/manager/GroupManager.class new file mode 100644 index 00000000..95bace76 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/manager/GroupManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class b/code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class new file mode 100644 index 00000000..6dd28505 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class b/code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class new file mode 100644 index 00000000..c998a8d3 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class differ diff --git a/code/link/link-backend/target/classes/com/link/im/model/ChatMessage.class b/code/link/link-backend/target/classes/com/link/im/model/ChatMessage.class new file mode 100644 index 00000000..69637e80 Binary files /dev/null and b/code/link/link-backend/target/classes/com/link/im/model/ChatMessage.class differ diff --git a/code/link/link-backend/target/classes/dbconfigs/G3HD.properties b/code/link/link-backend/target/classes/dbconfigs/G3HD.properties new file mode 100644 index 00000000..7506e981 --- /dev/null +++ b/code/link/link-backend/target/classes/dbconfigs/G3HD.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HY2025 +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/target/classes/dbconfigs/G3HD2.properties b/code/link/link-backend/target/classes/dbconfigs/G3HD2.properties new file mode 100644 index 00000000..bb2277ab --- /dev/null +++ b/code/link/link-backend/target/classes/dbconfigs/G3HD2.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HD +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/target/classes/spy.properties b/code/link/link-backend/target/classes/spy.properties new file mode 100644 index 00000000..fc9a6edb --- /dev/null +++ b/code/link/link-backend/target/classes/spy.properties @@ -0,0 +1,12 @@ +# ?? slf4j ?????? +appender=com.p6spy.engine.spy.appender.Slf4JLogger + +# ???? +logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat + +# ???? +logLevel=INFO + +# ?????????????SQL?? +excludecategories=info,debug,result,batch + diff --git a/code/link/link-backend/target/classes/test.html b/code/link/link-backend/target/classes/test.html new file mode 100644 index 00000000..3b245cb8 --- /dev/null +++ b/code/link/link-backend/target/classes/test.html @@ -0,0 +1,288 @@ + + + + + + WebSocket Demo — Netty + + + +
+

WebSocket Demo — Netty

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + +
+ +
状态: 未连接
+
+
+ +
+
+
+ + +
+
+ + +
+
+ +
+ + +
Sends: 0, Received: 0
+
+
+ +
+
+
Message Log
+
Time is local browser time
+
+
+ +
+
+ + + + diff --git a/code/link/link-fronted/.vscode/extensions.json b/code/link/link-fronted/.vscode/extensions.json deleted file mode 100644 index a7cea0b0..00000000 --- a/code/link/link-fronted/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["Vue.volar"] -} diff --git a/code/link/link-fronted/README.md b/code/link/link-fronted/README.md deleted file mode 100644 index 1511959c..00000000 --- a/code/link/link-fronted/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Vue 3 + Vite - -This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` - - - - diff --git a/code/link/link-fronted/test.html b/code/link/link-fronted/test.html deleted file mode 100644 index 1fd81587..00000000 --- a/code/link/link-fronted/test.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - 企业三栏聊天页面 - - - -
- - - - -
-
请选择聊天对象
-
- -
- -
-
企业A
-
欢迎使用企业聊天系统。
-
09:30
-
-
-
- -
-
我
-
收到,确认。
-
09:31
-
-
-
-
- - -
-
- - -
-

聊天详情

-
    -
  • 企业名称:企业A
  • -
  • 联系人:张三
  • -
  • 部门:技术部
  • -
  • 未读消息:2
  • -
  • 加入时间:2025-10-10
  • -
-
-
- - diff --git a/code/link/link-fronted/.gitignore b/code/link/link-frontend/.gitignore similarity index 86% rename from code/link/link-fronted/.gitignore rename to code/link/link-frontend/.gitignore index a547bf36..8b7e5021 100644 --- a/code/link/link-fronted/.gitignore +++ b/code/link/link-frontend/.gitignore @@ -13,8 +13,6 @@ dist-ssr *.local # Editor directories and files -.vscode/* -!.vscode/extensions.json .idea .DS_Store *.suo diff --git a/code/link/link-frontend/.vscode/settings.json b/code/link/link-frontend/.vscode/settings.json new file mode 100644 index 00000000..4472e953 --- /dev/null +++ b/code/link/link-frontend/.vscode/settings.json @@ -0,0 +1,51 @@ +{ + "editor.cursorSmoothCaretAnimation": "on", + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off", "fixable": true }, + { "rule": "format/*", "severity": "off", "fixable": true }, + { "rule": "*-indent", "severity": "off", "fixable": true }, + { "rule": "*-spacing", "severity": "off", "fixable": true }, + { "rule": "*-spaces", "severity": "off", "fixable": true }, + { "rule": "*-order", "severity": "off", "fixable": true }, + { "rule": "*-dangle", "severity": "off", "fixable": true }, + { "rule": "*-newline", "severity": "off", "fixable": true }, + { "rule": "*quotes", "severity": "off", "fixable": true }, + { "rule": "*semi", "severity": "off", "fixable": true } + ], + + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml", + "xml", + "gql", + "graphql", + "astro", + "svelte", + "css", + "less", + "scss", + "pcss", + "postcss" + ] +} diff --git a/code/link/link-frontend/.vscode/vue3.code-snippets b/code/link/link-frontend/.vscode/vue3.code-snippets new file mode 100644 index 00000000..1265d5e2 --- /dev/null +++ b/code/link/link-frontend/.vscode/vue3.code-snippets @@ -0,0 +1,16 @@ +{ + "Print Vue3 SFC": { + "scope": "vue", + "prefix": "v3", + "body": [ + "\n", + "\n", + "\n", + ], + }, +} \ No newline at end of file diff --git a/code/link/link-fronted/eslint.config.js b/code/link/link-frontend/eslint.config.js similarity index 72% rename from code/link/link-fronted/eslint.config.js rename to code/link/link-frontend/eslint.config.js index 6517d221..f0ebbbef 100644 --- a/code/link/link-fronted/eslint.config.js +++ b/code/link/link-frontend/eslint.config.js @@ -16,7 +16,7 @@ export default antfu({ }, rules: { - 'no-console': 'off', // 允许 console.log 不报错 - "prefer-promise-reject-errors": "off" + 'no-console': 'off', + 'prefer-promise-reject-errors': 'off', }, }) diff --git a/code/link/link-fronted/index.html b/code/link/link-frontend/index.html similarity index 67% rename from code/link/link-fronted/index.html rename to code/link/link-frontend/index.html index 701238c1..341de46e 100644 --- a/code/link/link-fronted/index.html +++ b/code/link/link-frontend/index.html @@ -4,9 +4,10 @@ - link-fronted + Vite + Vue + - +
diff --git a/code/link/link-frontend/package-lock.json b/code/link/link-frontend/package-lock.json new file mode 100644 index 00000000..26a0cfe4 --- /dev/null +++ b/code/link/link-frontend/package-lock.json @@ -0,0 +1,4022 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "axios": "^1.9.0", + "dayjs": "^1.11.13", + "element-plus": "^2.10.7", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.32.0", + "lodash-es": "^4.17.21", + "pinia": "^3.0.3", + "pinia-plugin-persistedstate": "^4.5.0", + "sortablejs": "^1.15.6", + "vue": "^3.5.13", + "vue-router": "^4.5.1", + "vue3-puzzle-vcode": "^1.1.7" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.6", + "postcss-cli": "^11.0.1", + "vite": "^6.3.5" + } + }, + "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/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.1", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.1", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", + "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", + "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", + "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", + "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", + "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", + "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", + "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", + "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", + "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", + "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", + "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", + "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", + "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", + "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", + "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", + "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", + "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", + "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", + "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", + "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.17", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.17.tgz", + "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", + "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/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/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "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/@vue/compiler-core": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.16.tgz", + "integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.2", + "@vue/shared": "3.5.16", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz", + "integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz", + "integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.2", + "@vue/compiler-core": "3.5.16", + "@vue/compiler-dom": "3.5.16", + "@vue/compiler-ssr": "3.5.16", + "@vue/shared": "3.5.16", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.3", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz", + "integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "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-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "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==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.16.tgz", + "integrity": "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.16.tgz", + "integrity": "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz", + "integrity": "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.16", + "@vue/runtime-core": "3.5.16", + "@vue/shared": "3.5.16", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.16.tgz", + "integrity": "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.16", + "@vue/shared": "3.5.16" + }, + "peerDependencies": { + "vue": "3.5.16" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.16.tgz", + "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", + "license": "MIT" + }, + "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/@vueuse/core/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/@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/@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/@vueuse/shared/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/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "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==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "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.9.0", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "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==", + "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==", + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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.3", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "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.30001735", + "electron-to-chromium": "^1.5.204", + "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/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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001737", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/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/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/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/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==", + "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==", + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "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==", + "license": "MIT" + }, + "node_modules/deep-pick-omit": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz", + "integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==" + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" + }, + "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/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/electron-to-chromium": { + "version": "1.5.209", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz", + "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==", + "dev": true, + "license": "ISC" + }, + "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==", + "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/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/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/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/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "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==", + "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==", + "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-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==", + "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-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==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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==", + "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/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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==", + "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==", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "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==", + "license": "Apache-2.0" + }, + "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==", + "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==", + "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==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.5", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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==", + "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": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "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/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": "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/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "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/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/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "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==", + "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==", + "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/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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==", + "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==", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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==", + "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/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==", + "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "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/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/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/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-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": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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==", + "license": "MIT" + }, + "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/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/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==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "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.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "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": "3.0.3", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "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/pinia-plugin-persistedstate": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.5.0.tgz", + "integrity": "sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==", + "dependencies": { + "deep-pick-omit": "^1.2.1", + "defu": "^6.1.4", + "destr": "^2.0.5" + }, + "peerDependencies": { + "@nuxt/kit": ">=3.0.0", + "@pinia/nuxt": ">=0.10.0", + "pinia": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@pinia/nuxt": { + "optional": true + }, + "pinia": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, + "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-cli": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/postcss-cli/-/postcss-cli-11.0.1.tgz", + "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.3.0", + "dependency-graph": "^1.0.0", + "fs-extra": "^11.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^5.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^5.0.0", + "tinyglobby": "^0.2.12", + "yargs": "^17.0.0" + }, + "bin": { + "postcss": "index.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-5.1.0.tgz", + "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1", + "yaml": "^2.4.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + } + } + }, + "node_modules/postcss-reporter": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-reporter/-/postcss-reporter-7.1.0.tgz", + "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "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/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "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==", + "license": "MIT", + "peer": true, + "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==", + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "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==", + "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": "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/readdirp/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/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-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.42.0.tgz", + "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.42.0", + "@rollup/rollup-android-arm64": "4.42.0", + "@rollup/rollup-darwin-arm64": "4.42.0", + "@rollup/rollup-darwin-x64": "4.42.0", + "@rollup/rollup-freebsd-arm64": "4.42.0", + "@rollup/rollup-freebsd-x64": "4.42.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", + "@rollup/rollup-linux-arm-musleabihf": "4.42.0", + "@rollup/rollup-linux-arm64-gnu": "4.42.0", + "@rollup/rollup-linux-arm64-musl": "4.42.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-musl": "4.42.0", + "@rollup/rollup-linux-s390x-gnu": "4.42.0", + "@rollup/rollup-linux-x64-gnu": "4.42.0", + "@rollup/rollup-linux-x64-musl": "4.42.0", + "@rollup/rollup-win32-arm64-msvc": "4.42.0", + "@rollup/rollup-win32-ia32-msvc": "4.42.0", + "@rollup/rollup-win32-x64-msvc": "4.42.0", + "fsevents": "~2.3.2" + } + }, + "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==", + "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/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sortablejs": { + "version": "1.15.6", + "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz", + "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==" + }, + "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/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "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==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "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/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==", + "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==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.16.tgz", + "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.16", + "@vue/compiler-sfc": "3.5.16", + "@vue/runtime-dom": "3.5.16", + "@vue/server-renderer": "3.5.16", + "@vue/shared": "3.5.16" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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==", + "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/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/vue3-puzzle-vcode": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/vue3-puzzle-vcode/-/vue3-puzzle-vcode-1.1.7.tgz", + "integrity": "sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "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==", + "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/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/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/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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/code/link/link-frontend/package.json b/code/link/link-frontend/package.json new file mode 100644 index 00000000..d6c29685 --- /dev/null +++ b/code/link/link-frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "@iconify/vue": "^5.0.0", + "axios": "^1.9.0", + "dayjs": "^1.11.13", + "element-plus": "^2.10.7", + "lodash-es": "^4.17.21", + "pinia": "^3.0.3", + "pinia-plugin-persistedstate": "^4.5.0", + "sortablejs": "^1.15.6", + "vue": "^3.5.13", + "vue-router": "^4.5.1", + "vue3-puzzle-vcode": "^1.1.7" + }, + "devDependencies": { + "@antfu/eslint-config": "^5.2.1", + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "eslint": "^9.34.0", + "eslint-plugin-format": "^1.0.1", + "postcss": "^8.5.6", + "postcss-cli": "^11.0.1", + "sass": "^1.93.2", + "vite": "^6.3.5" + } +} diff --git a/code/link/link-fronted/pnpm-lock.yaml b/code/link/link-frontend/pnpm-lock.yaml similarity index 77% rename from code/link/link-fronted/pnpm-lock.yaml rename to code/link/link-frontend/pnpm-lock.yaml index 437deba3..ee2e38d1 100644 --- a/code/link/link-fronted/pnpm-lock.yaml +++ b/code/link/link-frontend/pnpm-lock.yaml @@ -8,28 +8,70 @@ importers: .: dependencies: + '@element-plus/icons-vue': + specifier: ^2.3.2 + version: 2.3.2(vue@3.5.22(typescript@5.9.3)) + '@iconify/vue': + specifier: ^5.0.0 + version: 5.0.0(vue@3.5.22(typescript@5.9.3)) + axios: + specifier: ^1.9.0 + version: 1.12.2 + dayjs: + specifier: ^1.11.13 + version: 1.11.18 + element-plus: + specifier: ^2.10.7 + version: 2.11.4(vue@3.5.22(typescript@5.9.3)) + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + pinia: + specifier: ^3.0.3 + version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + pinia-plugin-persistedstate: + specifier: ^4.5.0 + version: 4.5.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))) + sortablejs: + specifier: ^1.15.6 + version: 1.15.6 vue: - specifier: ^3.5.22 + specifier: ^3.5.13 version: 3.5.22(typescript@5.9.3) vue-router: specifier: ^4.5.1 version: 4.5.1(vue@3.5.22(typescript@5.9.3)) + vue3-puzzle-vcode: + specifier: ^1.1.7 + version: 1.1.7 devDependencies: '@antfu/eslint-config': specifier: ^5.2.1 version: 5.4.1(@vue/compiler-sfc@3.5.22)(eslint-plugin-format@1.0.2(eslint@9.37.0))(eslint@9.37.0)(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: ^6.0.1 - version: 6.0.1(vite@7.1.9(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + specifier: ^5.2.3 + version: 5.2.4(vite@6.3.6(sass@1.93.2)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) eslint: specifier: ^9.34.0 version: 9.37.0 eslint-plugin-format: specifier: ^1.0.1 version: 1.0.2(eslint@9.37.0) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + postcss-cli: + specifier: ^11.0.1 + version: 11.0.1(postcss@8.5.6) + sass: + specifier: ^1.93.2 + version: 1.93.2 vite: - specifier: ^7.1.7 - version: 7.1.9(yaml@2.8.1) + specifier: ^6.3.5 + version: 6.3.6(sass@1.93.2)(yaml@2.8.1) packages: @@ -114,6 +156,10 @@ packages: '@clack/prompts@0.11.0': resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + '@dprint/formatter@0.3.0': resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==} @@ -123,6 +169,11 @@ packages: '@dprint/toml@0.6.4': resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==} + '@element-plus/icons-vue@2.3.2': + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} + peerDependencies: + vue: ^3.2.0 + '@es-joy/jsdoccomment@0.50.2': resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} engines: {node: '>=18'} @@ -352,6 +403,15 @@ packages: resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -368,6 +428,14 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/vue@5.0.0': + resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==} + peerDependencies: + vue: '>=3' + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -383,6 +451,94 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + '@pkgr/core@0.1.2': resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -391,9 +547,6 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rolldown/pluginutils@1.0.0-beta.29': - resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} - '@rollup/rollup-android-arm-eabi@4.52.4': resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} cpu: [arm] @@ -428,56 +581,67 @@ packages: resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.4': resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.4': resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.4': resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.4': resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.4': resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.4': resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.4': resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.4': resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.4': resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.4': resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.4': resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} @@ -510,6 +674,9 @@ packages: peerDependencies: eslint: '>=9.0.0' + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -519,6 +686,12 @@ packages: '@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/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -528,6 +701,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + '@typescript-eslint/eslint-plugin@8.46.0': resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -587,11 +763,11 @@ packages: resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue@6.0.1': - resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} - engines: {node: ^20.19.0 || >=22.12.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 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 '@vitest/eslint-plugin@1.3.16': @@ -621,6 +797,15 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/devtools-api@7.7.7': + resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} + + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} + + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} + '@vue/reactivity@3.5.22': resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==} @@ -638,6 +823,15 @@ packages: '@vue/shared@3.5.22': resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -651,6 +845,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -659,6 +857,10 @@ packages: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -666,6 +868,22 @@ packages: 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.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -673,6 +891,13 @@ packages: resolution: {integrity: sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==} hasBin: true + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + birpc@2.6.1: + resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -699,6 +924,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + 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'} @@ -719,6 +948,14 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + 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'} + ci-info@4.3.1: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} @@ -727,6 +964,10 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -734,6 +975,10 @@ packages: 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'} + comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} @@ -747,6 +992,10 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + core-js-compat@3.46.0: resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} @@ -762,6 +1011,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -777,10 +1029,32 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-pick-omit@1.2.1: + resolution: {integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dependency-graph@1.0.0: + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + engines: {node: '>=4'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -788,9 +1062,21 @@ packages: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + electron-to-chromium@1.5.234: resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==} + element-plus@2.11.4: + resolution: {integrity: sha512-sLq+Ypd0cIVilv8wGGMEGvzRVBBsRpJjnAS5PsI/1JU1COZXqzH3N1UYMUc/HCdvdjf6dfrBy80Sj7KcACsT7w==} + peerDependencies: + vue: ^3.2.0 + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -803,6 +1089,22 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + 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'} + esbuild@0.25.10: resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} @@ -812,6 +1114,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1091,15 +1396,50 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + 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==} + + 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-tsconfig@4.12.0: resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} @@ -1129,6 +1469,10 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + 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==} @@ -1139,6 +1483,21 @@ packages: 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'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1147,6 +1506,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1159,6 +1521,10 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} @@ -1167,6 +1533,10 @@ packages: 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'} @@ -1175,6 +1545,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1217,6 +1591,9 @@ packages: resolution: {integrity: sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1224,6 +1601,10 @@ packages: 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'} + local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -1232,6 +1613,16 @@ packages: 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.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -1247,6 +1638,10 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -1283,6 +1678,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1378,6 +1776,14 @@ packages: 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-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==} @@ -1385,6 +1791,9 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -1403,9 +1812,23 @@ packages: resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} engines: {node: '>=18'} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-releases@2.0.23: resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + 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==} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1452,6 +1875,9 @@ packages: 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==} @@ -1463,6 +1889,33 @@ packages: 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-plugin-persistedstate@4.5.0: + resolution: {integrity: sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: '>=3.0.0' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@3.0.3: + resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -1476,10 +1929,41 @@ packages: pnpm-workspace-yaml@1.2.0: resolution: {integrity: sha512-4CnZHmLSaprRnIm2iQ27Zl1cWPRHdX7Ehw7ckRwujoPKCk2QAz4agsA2MbTodg4sgbqYfJ68ULT+Q5A8dU+Mow==} + postcss-cli@11.0.1: + resolution: {integrity: sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + postcss: ^8.0.0 + + postcss-load-config@5.1.0: + resolution: {integrity: sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + + postcss-reporter@7.1.0: + resolution: {integrity: sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.1.0 + 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} @@ -1497,6 +1981,13 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + + 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'} @@ -1507,6 +1998,17 @@ packages: 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'} + refa@0.12.1: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -1523,6 +2025,10 @@ packages: resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true + 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'} @@ -1534,6 +2040,9 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rollup@4.52.4: resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1542,6 +2051,11 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sass@1.93.2: + resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} + engines: {node: '>=14.0.0'} + hasBin: true + scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -1562,6 +2076,13 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + sortablejs@1.15.6: + resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1575,6 +2096,18 @@ packages: spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-indent@4.1.0: resolution: {integrity: sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==} engines: {node: '>=12'} @@ -1583,6 +2116,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + 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'} @@ -1599,6 +2136,9 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + thenby@1.3.4: + resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} + tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -1656,6 +2196,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -1668,19 +2212,19 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@7.1.9: - resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} - engines: {node: ^20.19.0 || >=22.12.0} + vite@6.3.6: + resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 jiti: '>=1.21.0' - less: ^4.0.0 + less: '*' lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -1708,6 +2252,17 @@ packages: yaml: optional: true + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue-eslint-parser@10.2.0: resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1719,6 +2274,9 @@ packages: peerDependencies: vue: ^3.2.0 + vue3-puzzle-vcode@1.1.7: + resolution: {integrity: sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g==} + vue@3.5.22: resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==} peerDependencies: @@ -1736,10 +2294,18 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + 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'} + yaml-eslint-parser@1.3.0: resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} engines: {node: ^14.17.0 || >=16.0.0} @@ -1749,6 +2315,14 @@ packages: 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'} @@ -1835,12 +2409,18 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + '@ctrl/tinycolor@3.6.1': {} + '@dprint/formatter@0.3.0': {} '@dprint/markdown@0.17.8': {} '@dprint/toml@0.6.4': {} + '@element-plus/icons-vue@2.3.2(vue@3.5.22(typescript@5.9.3))': + dependencies: + vue: 3.5.22(typescript@5.9.3) + '@es-joy/jsdoccomment@0.50.2': dependencies: '@types/estree': 1.0.8 @@ -2016,6 +2596,17 @@ snapshots: '@eslint/core': 0.16.0 levn: 0.4.1 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2027,6 +2618,13 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': {} + + '@iconify/vue@5.0.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.22(typescript@5.9.3) + '@jridgewell/sourcemap-codec@1.5.5': {} '@nodelib/fs.scandir@2.1.5': @@ -2041,12 +2639,71 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + '@pkgr/core@0.1.2': {} '@pkgr/core@0.2.9': {} - '@rolldown/pluginutils@1.0.0-beta.29': {} - '@rollup/rollup-android-arm-eabi@4.52.4': optional: true @@ -2123,6 +2780,8 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.3 + '@sxzz/popperjs-es@2.11.7': {} + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -2131,6 +2790,12 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -2139,6 +2804,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.16': {} + '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0)(typescript@5.9.3))(eslint@9.37.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2232,10 +2899,9 @@ snapshots: '@typescript-eslint/types': 8.46.0 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-vue@6.0.1(vite@7.1.9(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.3.6(sass@1.93.2)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.9(yaml@2.8.1) + vite: 6.3.6(sass@1.93.2)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) '@vitest/eslint-plugin@1.3.16(eslint@9.37.0)(typescript@5.9.3)': @@ -2280,6 +2946,24 @@ snapshots: '@vue/devtools-api@6.6.4': {} + '@vue/devtools-api@7.7.7': + dependencies: + '@vue/devtools-kit': 7.7.7 + + '@vue/devtools-kit@7.7.7': + dependencies: + '@vue/devtools-shared': 7.7.7 + birpc: 2.6.1 + 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/reactivity@3.5.22': dependencies: '@vue/shared': 3.5.22 @@ -2304,6 +2988,25 @@ snapshots: '@vue/shared@3.5.22': {} + '@vueuse/core@9.13.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.22(typescript@5.9.3)) + vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/shared@9.13.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -2317,20 +3020,53 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansis@4.2.0: {} + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + are-docs-informative@0.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.26.3 + caniuse-lite: 1.0.30001749 + 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.12.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.15: {} + binary-extensions@2.3.0: {} + + birpc@2.6.1: {} + boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -2358,6 +3094,11 @@ snapshots: cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} caniuse-lite@1.0.30001749: {} @@ -2373,18 +3114,44 @@ snapshots: character-entities@2.0.2: {} + 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 + ci-info@4.3.1: {} clean-regexp@1.0.0: dependencies: escape-string-regexp: 1.0.5 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.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 + comment-parser@1.4.1: {} concat-map@0.0.1: {} @@ -2393,6 +3160,10 @@ snapshots: confbox@0.2.2: {} + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + core-js-compat@3.46.0: dependencies: browserslist: 4.26.3 @@ -2407,6 +3178,8 @@ snapshots: csstype@3.1.3: {} + dayjs@1.11.18: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2417,16 +3190,58 @@ snapshots: deep-is@0.1.4: {} + deep-pick-omit@1.2.1: {} + + defu@6.1.4: {} + + delayed-stream@1.0.0: {} + + dependency-graph@1.0.0: {} + dequal@2.0.3: {} + destr@2.0.5: {} + + detect-libc@1.0.3: + optional: true + devlop@1.1.0: dependencies: dequal: 2.0.3 diff-sequences@27.5.1: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + electron-to-chromium@1.5.234: {} + element-plus@2.11.4(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.2(vue@3.5.22(typescript@5.9.3)) + '@floating-ui/dom': 1.7.4 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.20 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.5.22(typescript@5.9.3)) + async-validator: 4.2.5 + dayjs: 1.11.18 + 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.22(typescript@5.9.3) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@8.0.0: {} + empathic@2.0.0: {} enhanced-resolve@5.18.3: @@ -2436,6 +3251,21 @@ snapshots: entities@4.5.0: {} + 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 + esbuild@0.25.10: optionalDependencies: '@esbuild/aix-ppc64': 0.25.10 @@ -2467,6 +3297,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -2568,7 +3400,7 @@ snapshots: eslint: 9.37.0 eslint-compat-utils: 0.6.5(eslint@9.37.0) eslint-json-compat-utils: 0.2.1(eslint@9.37.0)(jsonc-eslint-parser@2.4.1) - espree: 10.4.0 + espree: 9.6.1 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.1 natural-compare: 1.4.0 @@ -2820,11 +3652,51 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + + 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 + format@0.2.2: {} + fraction.js@4.3.7: {} + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fsevents@2.3.3: optional: true + function-bind@1.1.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-tsconfig@4.12.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -2847,16 +3719,32 @@ snapshots: globrex@0.1.2: {} + 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 + + hookable@5.5.3: {} + ignore@5.3.2: {} ignore@7.0.5: {} + immutable@5.1.3: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2866,18 +3754,26 @@ snapshots: indent-string@5.0.0: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-builtin-module@5.0.0: dependencies: builtin-modules: 5.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-number@7.0.0: {} + is-what@4.1.16: {} + isexe@2.0.0: {} js-yaml@4.1.0: @@ -2907,6 +3803,12 @@ snapshots: espree: 9.6.1 semver: 7.7.3 + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2916,6 +3818,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lilconfig@3.1.3: {} + local-pkg@1.1.2: dependencies: mlly: 1.8.0 @@ -2926,6 +3830,14 @@ snapshots: 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.merge@4.6.2: {} lodash@4.17.21: {} @@ -2938,6 +3850,8 @@ snapshots: markdown-table@3.0.4: {} + math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -3051,6 +3965,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + memoize-one@6.0.0: {} + merge2@1.4.1: {} micromark-core-commonmark@2.0.3: @@ -3256,6 +4172,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -3264,6 +4186,8 @@ snapshots: dependencies: brace-expansion: 2.0.2 + mitt@3.0.1: {} + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -3279,8 +4203,17 @@ snapshots: natural-orderby@5.0.0: {} + node-addon-api@7.1.1: + optional: true + node-releases@2.0.23: {} + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -3326,12 +4259,31 @@ snapshots: 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-plugin-persistedstate@4.5.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))): + dependencies: + deep-pick-omit: 1.2.1 + defu: 6.1.4 + destr: 2.0.5 + optionalDependencies: + pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + + pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 7.7.7 + vue: 3.5.22(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -3350,11 +4302,44 @@ snapshots: dependencies: yaml: 2.8.1 + postcss-cli@11.0.1(postcss@8.5.6): + dependencies: + chokidar: 3.6.0 + dependency-graph: 1.0.0 + fs-extra: 11.3.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-load-config: 5.1.0(postcss@8.5.6) + postcss-reporter: 7.1.0(postcss@8.5.6) + pretty-hrtime: 1.0.3 + read-cache: 1.0.0 + slash: 5.1.0 + tinyglobby: 0.2.15 + yargs: 17.7.2 + transitivePeerDependencies: + - jiti + - tsx + + postcss-load-config@5.1.0(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.1 + optionalDependencies: + postcss: 8.5.6 + + postcss-reporter@7.1.0(postcss@8.5.6): + dependencies: + picocolors: 1.1.1 + postcss: 8.5.6 + thenby: 1.3.4 + 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 @@ -3369,12 +4354,26 @@ snapshots: prettier@3.6.2: {} + pretty-hrtime@1.0.3: {} + + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} quansync@0.2.11: {} 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: {} + refa@0.12.1: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3390,12 +4389,16 @@ snapshots: dependencies: jsesc: 3.0.2 + require-directory@2.1.1: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} reusify@1.1.0: {} + rfdc@1.4.1: {} + rollup@4.52.4: dependencies: '@types/estree': 1.0.8 @@ -3428,6 +4431,14 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sass@1.93.2: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3444,6 +4455,10 @@ snapshots: sisteransi@1.0.5: {} + slash@5.1.0: {} + + sortablejs@1.15.6: {} + source-map-js@1.2.1: {} spdx-exceptions@2.5.0: {} @@ -3455,10 +4470,26 @@ snapshots: spdx-license-ids@3.0.22: {} + speakingurl@14.0.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-indent@4.1.0: {} strip-json-comments@3.1.1: {} + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3474,6 +4505,8 @@ snapshots: tapable@2.3.0: {} + thenby@1.3.4: {} + tinyexec@1.0.1: {} tinyglobby@0.2.15: @@ -3529,6 +4562,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + universalify@2.0.1: {} + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: browserslist: 4.26.3 @@ -3541,7 +4576,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@7.1.9(yaml@2.8.1): + vite@6.3.6(sass@1.93.2)(yaml@2.8.1): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -3551,8 +4586,13 @@ snapshots: tinyglobby: 0.2.15 optionalDependencies: fsevents: 2.3.3 + sass: 1.93.2 yaml: 2.8.1 + vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)): + dependencies: + vue: 3.5.22(typescript@5.9.3) + vue-eslint-parser@10.2.0(eslint@9.37.0): dependencies: debug: 4.4.3 @@ -3570,6 +4610,8 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.22(typescript@5.9.3) + vue3-puzzle-vcode@1.1.7: {} + vue@3.5.22(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.22 @@ -3586,8 +4628,16 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + xml-name-validator@4.0.0: {} + y18n@5.0.8: {} + yaml-eslint-parser@1.3.0: dependencies: eslint-visitor-keys: 3.4.3 @@ -3595,6 +4645,18 @@ snapshots: yaml@2.8.1: {} + 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: {} zwitch@2.0.4: {} diff --git a/code/link/link-frontend/postcss.config.cjs b/code/link/link-frontend/postcss.config.cjs new file mode 100644 index 00000000..3277144d --- /dev/null +++ b/code/link/link-frontend/postcss.config.cjs @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require('autoprefixer')({ + overrideBrowserslist: ['> 0.5%', 'last 2 versions', 'IE 10'], + }), + ], +} diff --git a/code/link/link-frontend/public/Thumbs.db b/code/link/link-frontend/public/Thumbs.db new file mode 100644 index 00000000..4a527338 Binary files /dev/null and b/code/link/link-frontend/public/Thumbs.db differ diff --git a/code/link/link-frontend/public/logo.ico b/code/link/link-frontend/public/logo.ico new file mode 100644 index 00000000..cb52fd00 Binary files /dev/null and b/code/link/link-frontend/public/logo.ico differ diff --git a/code/link/link-fronted/public/vite.svg b/code/link/link-frontend/public/vite.svg similarity index 100% rename from code/link/link-fronted/public/vite.svg rename to code/link/link-frontend/public/vite.svg diff --git a/code/link/link-fronted/src/App.vue b/code/link/link-frontend/src/App.vue similarity index 72% rename from code/link/link-fronted/src/App.vue rename to code/link/link-frontend/src/App.vue index 1ceb4b8a..c97b1623 100644 --- a/code/link/link-fronted/src/App.vue +++ b/code/link/link-frontend/src/App.vue @@ -4,6 +4,3 @@ - - diff --git a/code/link/link-fronted/src/assets/vue.svg b/code/link/link-frontend/src/assets/vue.svg similarity index 100% rename from code/link/link-fronted/src/assets/vue.svg rename to code/link/link-frontend/src/assets/vue.svg diff --git a/code/link/link-frontend/src/components/avatar.vue b/code/link/link-frontend/src/components/avatar.vue new file mode 100644 index 00000000..e0d7864e --- /dev/null +++ b/code/link/link-frontend/src/components/avatar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/code/link/link-frontend/src/components/icon.vue b/code/link/link-frontend/src/components/icon.vue new file mode 100644 index 00000000..9e340aac --- /dev/null +++ b/code/link/link-frontend/src/components/icon.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/code/link/link-frontend/src/main.js b/code/link/link-frontend/src/main.js new file mode 100644 index 00000000..71ca5732 --- /dev/null +++ b/code/link/link-frontend/src/main.js @@ -0,0 +1,18 @@ +import './style.css' + +import { createApp } from 'vue' +import App from './App.vue' +import router from './router' + +import ElementPlus from 'element-plus' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import 'element-plus/dist/index.css' + +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' +const pinia = createPinia() +pinia.use(piniaPluginPersistedstate) + +const app = createApp(App) + +app.use(router).use(pinia).use(ElementPlus, { locale: zhCn }).mount('#app') diff --git a/code/link/link-frontend/src/router/index.js b/code/link/link-frontend/src/router/index.js new file mode 100644 index 00000000..f89695ad --- /dev/null +++ b/code/link/link-frontend/src/router/index.js @@ -0,0 +1,32 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Im from '@/views/im/index.vue' + +const routes = [ + { + path: '/', + redirect: '/im', + }, + { + path: '/im', + component: Im, + }, +] + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +// 全局前置守卫 +router.beforeEach((to, from, next) => { + // 设置页面标题 + document.title = to.meta.title ? `${to.meta.title} - 系统名称` : '系统名称' + + // 通过检查,继续跳转 + next() +}) + +router.afterEach(() => { +}) + +export default router diff --git a/code/link/link-frontend/src/stores/userStore.js b/code/link/link-frontend/src/stores/userStore.js new file mode 100644 index 00000000..419ec6f7 --- /dev/null +++ b/code/link/link-frontend/src/stores/userStore.js @@ -0,0 +1,43 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useUserStore = defineStore( + 'userInfo', + () => { + + const userInfo = ref({ + orgid: '', + userid: '', + token: '', + }) + + // 设置用户信息 + function setUserInfo(orgid, userid, token) { + userInfo.value.orgid = orgid + userInfo.value.userid = userid + userInfo.value.token = token + } + + // 重置用户信息 + function resetUserInfo() { + userInfo.value.token = '' + } + + // 判断是否登录:三个都存在才算登录 + function isLogined() { + return !!userInfo.value.orgid && !!userInfo.value.userid && !!userInfo.value.token + } + + return { + userInfo, + setUserInfo, + resetUserInfo, + isLogined, + } + }, + { + persist: { + paths: ['userInfo'], + }, + }, +) diff --git a/code/link/link-frontend/src/style.css b/code/link/link-frontend/src/style.css new file mode 100644 index 00000000..3650bbf9 --- /dev/null +++ b/code/link/link-frontend/src/style.css @@ -0,0 +1,11 @@ +html, +body, +#app { + height: 100%; +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} diff --git a/code/link/link-frontend/src/utils/axios.js b/code/link/link-frontend/src/utils/axios.js new file mode 100644 index 00000000..0a76188b --- /dev/null +++ b/code/link/link-frontend/src/utils/axios.js @@ -0,0 +1,34 @@ +// src/utils/axios.js +import axios from 'axios' + +const instance = axios.create({ + baseURL: 'http://localhost:8089/api', // 后端接口地址 + timeout: 5000 +}) + +// 请求拦截器:自动添加 token +instance.interceptors.request.use( + config => { + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + error => Promise.reject(error) +) + +// 响应拦截器:统一处理错误或过期 token +instance.interceptors.response.use( + response => response, + error => { + if (error.response && error.response.status === 401) { + alert('登录失效,请重新登录') + window.localStorage.removeItem('token') + window.location.href = '/login' + } + return Promise.reject(error) + } +) + +export default instance diff --git a/code/link/link-frontend/src/utils/dataDiffUtils.js b/code/link/link-frontend/src/utils/dataDiffUtils.js new file mode 100644 index 00000000..771ac9a1 --- /dev/null +++ b/code/link/link-frontend/src/utils/dataDiffUtils.js @@ -0,0 +1,493 @@ +import axios from '@/utils/axios' +import { ref } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' + + +const saving = ref(false) +const error = ref(null) + +/* 比对并分类数据变更 + * @param {Array} currentData 变更后的数据数组 + * @param {Array} originalData 原始数据数组 + * @param {String} primaryKey 主键字段名 + * @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean } + */ +export const compareAndClassifyData = (currentData, originalData, primaryKey) => { + const result = { + insertdata: [], + updatedata: [], + deletedata: [], + hasChanges: false, // 默认无变动 + } + + // 1. 找出新增的数据 + result.insertdata = currentData.filter( + (row) => + !row[primaryKey] || + !originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]), + ) + + // 2. 找出修改的数据 + result.updatedata = currentData.reduce((acc, row) => { + const rowId = row[primaryKey] + if (!rowId) return acc + + const originalRow = originalData.find((r) => r[primaryKey] === rowId) + if (!originalRow) return acc + + const changedFields = {} + Object.keys(row).forEach((key) => { + if (key !== primaryKey && !isEqual(row[key], originalRow[key])) { + changedFields[key] = row[key] + } + }) + + if (Object.keys(changedFields).length > 0) { + acc.push({ + [primaryKey]: rowId, + ...changedFields, + }) + } + + return acc + }, []) + + // 3. 找出删除的数据 + result.deletedata = originalData.filter( + (originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]), + ) + + // 4. 是否有任何变动 + result.hasChanges = + result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0 + + return result +} + +/** + * 在数组对象的每个元素中覆盖式填入 JSON 对象的值 + * @param {Array} arr - 目标数组(将被修改) + * @param {Object} json - 包含要添加/覆盖的键值对的 JSON 对象 + * @returns {Array} 修改后的数组 + * @throws {Error} 当参数类型不正确时抛出错误 + */ +export function fillDefaultArray(arr, json) { + // 基本数据验证 + if (!Array.isArray(arr)) throw new Error('第一个参数必须是数组') + if (typeof json !== 'object' || json === null || Array.isArray(json)) { + throw new Error('第二个参数必须是普通对象(JSON对象)') + } + + // 覆盖式填充每个元素 + return arr.map((item) => { + // 只处理对象元素 + if (typeof item === 'object' && item !== null) { + return { ...item, ...json } // 覆盖已有属性 + } + return item // 非对象元素保持不变 + }) +} + +/* 比对并分类数据变更 + * @param {Array} currentData 变更后的数据数组 + * @param {Array} originalData 原始数据数组 + * @param {String} primaryKey 主键字段名 + * @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean } + */ +export const compareAndClassifyData1 = ( + currentData, + originalData, + primaryKey, + extraFields = {}, +) => { + const result = { + insertdata: [], + updatedata: [], + deletedata: [], + hasChanges: false, // 默认无变动 + } + + // 1. 找出新增的数据 + result.insertdata = currentData + .filter( + (row) => + !row[primaryKey] || + !originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]), + ) + .map((row) => ({ + ...row, + ...extraFields, // ✅ 添加新字段 + })) + + // 2. 找出修改的数据 + result.updatedata = currentData.reduce((acc, row) => { + const rowId = row[primaryKey] + if (!rowId) return acc + + const originalRow = originalData.find((r) => r[primaryKey] === rowId) + if (!originalRow) return acc + + const changedFields = {} + Object.keys(row).forEach((key) => { + if (key !== primaryKey && !isEqual(row[key], originalRow[key])) { + changedFields[key] = row[key] + } + }) + + if (Object.keys(changedFields).length > 0) { + acc.push({ + [primaryKey]: rowId, + ...changedFields, + }) + } + + return acc + }, []) + + // 3. 找出删除的数据 + result.deletedata = originalData.filter( + (originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]), + ) + + // 4. 是否有任何变动 + result.hasChanges = + result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0 + + return result +} + +/** + * 深度比较两个值是否相等 + * @param {*} a 第一个值 + * @param {*} b 第二个值 + * @returns {Boolean} 是否相等 + */ +export const isEqual = (a, b) => { + // 处理基本类型和null/undefined + if (a === b) return true + if (typeof a !== typeof b) return false + if (typeof a !== 'object' || a === null || b === null) { + return a === b + } + + // 处理数组 + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) return false + return a.every((item, i) => isEqual(item, b[i])) + } + + // 处理对象 + const keysA = Object.keys(a) + const keysB = Object.keys(b) + + if (keysA.length !== keysB.length) return false + + return keysA.every((key) => isEqual(a[key], b[key])) +} + +/** + * 检测并提取变更的字段(跳过v_开头的字段) + * @param {Object} currentData 当前数据 + * @param {Object} originalData 原始数据 + * @param {String} primaryKey 主键字段名 + * @param {String} primaryKeyValue 主键值 + * @returns {Object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Object } + */ +export const detectChanges = (currentData, originalData, primaryKey, primaryKeyValue) => { + const updateValue = {} + + // 遍历所有字段检测变更 + Object.keys(currentData).forEach((key) => { + // 跳过以v_开头的字段 + if (key.startsWith('v_')) return + + if (key === 'b_type') { + const currentStr = normalizeOwnerCustomer(currentData[key]) + const originalStr = normalizeOwnerCustomer(originalData[key]) + + if (currentStr !== originalStr) { + updateValue[key] = currentStr // 保存时格式化为逗号分隔字符串 + } + } else { + // 比较前,先将 undefined 转换为空字符串 + const currentValue = currentData[key] === undefined ? '' : currentData[key] + const originalValue = originalData[key] === undefined ? '' : originalData[key] + + // 使用JSON.stringify进行深度比较 + if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) { + updateValue[key] = currentValue + } + } + }) + + // 如果没有有效变更 + if (Object.keys(updateValue).length === 0) { + return { hasChanges: true, updateData: [] } + } + + // 返回变更数据和主键 + return { + hasChanges: true, + updateData: [ + { + [primaryKey]: primaryKeyValue, + ...updateValue, + }, + ], + } +} + +/** + * 检测并提取变更的字段(跳过v_开头的字段和指定排除字段) + * @param {Object} currentData 当前数据 + * @param {Object} originalData 原始数据 + * @param {Array|string} primaryKeys 主键字段名或数组 + * @param {Array} excludeKeys 可选,排除的字段数组 + * @returns {Object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Array } + */ +export const detectChangesFields = (currentData, originalData, primaryKeys, excludeKeys = []) => { + const updateValue = {} + + Object.keys(currentData).forEach((key) => { + // 跳过以v_开头的字段 + if (key.startsWith('v_')) return + + // 跳过排除字段 + if (excludeKeys.includes(key)) return + + const currentValue = currentData[key] === undefined ? '' : currentData[key] + const originalValue = originalData[key] === undefined ? '' : originalData[key] + + if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) { + updateValue[key] = currentValue + } + }) + + if (Object.keys(updateValue).length === 0) { + return { hasChanges: false, updateData: [] } + } + + // 构建主键字段对象 + const primaryKeyObj = {} + if (Array.isArray(primaryKeys)) { + primaryKeys.forEach((key) => { + primaryKeyObj[key] = originalData[key] + }) + } else { + primaryKeyObj[primaryKeys] = originalData[primaryKeys] + } + + return { + hasChanges: true, + updateData: [ + { + ...primaryKeyObj, + ...updateValue, + }, + ], + } +} + +const normalizeOwnerCustomer = (val) => { + if (typeof val === 'string') { + return val.split(',').filter(Boolean).join(',') // '001,002,003' + } + + if (Array.isArray(val)) { + return val.map((item) => (Array.isArray(item) ? item[0] : item)).join(',') + } + return '' +} + +export async function buildDeleteRequest(tableName, pkColumn, whereClause) { + const params = { + tableName, + pkColumn, + whereClause, + } + + const result = await axios.get('/pagdetail', { params }) + const records = result.data?.data?.records || [] + + if (records.length > 0) { + let deletes = records.map((item) => ({ + [pkColumn]: item[pkColumn], // 动态取主键 + })) + + return { + tableName, + keyField: pkColumn, + inserts: [], + updates: [], + deletes, + } + } + return null +} + +export const getDataBySql = async (sql) => { + try { + const response = await axios.get('/selectlistbysql', { + params: { sql } // GET 请求参数 + }) + if (response.data?.success) { + return response.data.data + } else { + console.error('查询失败:', response.data?.message) + return [] + } + } catch (err) { + console.error('接口异常:', err) + return [] + } +} + + + +/** + * 计算已收金额(折算成目标币种) + * @param {Array} subtable 子表数据 + * @param {String} targetMoney 目标币种 + * @param {Number} exrate 汇率 + * @returns {Number} 折算后的总金额 + */ +export function calcAlreadySumAmount(subtable, targetMoney, exrate = 1) { + const moneyArray = getMoneyArray(subtable) + let Fxzamount = 0 + + moneyArray.forEach(item => { + if (item.moneyname === targetMoney) { + Fxzamount += Math.round(Number(item.amount_xz) * 1000) / 1000 + } else { + if (targetMoney === 'RMB') { + Fxzamount += Math.round(Number(item.amount_xz) * exrate * 1000) / 1000 + } else { + Fxzamount += Math.round(Number(item.amount_xz) / exrate * 1000) / 1000 + } + } + }) + + return Math.round(Fxzamount * 10000) / 10000 +} + +/** + * 按币种汇总子表金额 + * @param {Array} subtable 子表数据 + * @returns {Array} 形如 [{ moneyname: 'USD', amount_xz: 100 }, ...] + */ +function getMoneyArray(subtable) { + const rows = subtable || [] + const moneyMap = new Map() + + rows.forEach(row => { + let xzamount = 0 + if (row.mx_type_id === '应付') { + xzamount = 0 - parseFloat(row.mx_xzamount || 0) + } + if (row.mx_type_id === '应收') { + xzamount = parseFloat(row.mx_xzamount || 0) + } + + if (row.mx_moneytype_id) { + const prev = moneyMap.get(row.mx_moneytype_id) || 0 + moneyMap.set(row.mx_moneytype_id, prev + xzamount) + } + }) + + return Array.from(moneyMap.entries()).map(([moneyname, amount]) => ({ + moneyname, + amount_xz: amount + })) +} + + +export const handleUpdateWidthEdit = (newWidth, oldWidth, column, columns) => { + const index = columns.findIndex(c => c.prop === column.property) + if (index >= 0) { + const newColumns = [...columns] + newColumns[index] = { + ...newColumns[index], + edit_width: parseInt(newWidth) || newWidth // 确保是数字 + } + return newColumns + } + return columns +} + +export const handleSaveColumnEdit = async (columns, tableName) => { + try { + saving.value = true + error.value = '' + const newColumns = columns.map(item => ({ + ...item, + col_TableName: tableName // 或者你要的默认值 + })) + + const response = await axios.post('/saveColumnEdit', newColumns, { + headers: { 'Content-Type': 'application/json' } + }) + if (response.data?.success) { + ElMessage.success('保存成功') + } else { + throw new Error(response.data?.message || '保存失败') + } + } catch (err) { + error.value = `保存失败: ${err.message}` + ElMessage.error('保存失败') + } finally { + saving.value = false + } +} + +export const handleUpdateWidth = (newWidth, oldWidth, column, columns) => { + const index = columns.findIndex(c => c.prop === column.property) + if (index >= 0) { + const newColumns = [...columns] + newColumns[index] = { + ...newColumns[index], + width: parseInt(newWidth) || newWidth // 确保是数字 + } + return newColumns + } + return columns +} + +export const handleSaveColumn = async (columns, tableName) => { + try { + saving.value = true + error.value = '' + const newColumns = columns.map(item => ({ + ...item, + col_TableName: tableName // 或者你要的默认值 + })) + + const response = await axios.post('/saveColumn', newColumns, { + headers: { 'Content-Type': 'application/json' } + }) + if (response.data?.success) { + ElMessage.success('保存成功') + } else { + throw new Error(response.data?.message || '保存失败') + } + } catch (err) { + error.value = `保存失败: ${err.message}` + ElMessage.error('保存失败') + } finally { + saving.value = false + } +} + +// 默认导出所有方法 +export default { + isEqual, + compareAndClassifyData, + detectChanges, + buildDeleteRequest, + fillDefaultArray, + getDataBySql, + calcAlreadySumAmount, + handleUpdateWidthEdit, + handleSaveColumnEdit, + handleUpdateWidth, + handleSaveColumn +} diff --git a/code/link/link-frontend/src/utils/dodata.js b/code/link/link-frontend/src/utils/dodata.js new file mode 100644 index 00000000..2a8e7328 --- /dev/null +++ b/code/link/link-frontend/src/utils/dodata.js @@ -0,0 +1,203 @@ +import axios from '@/utils/axios' +import { ref } from 'vue' + + + +const loading = ref(false) +const error = ref(null) + +// 获取表格列配置 +export const getTableColumns = async (tableName) => { + try { + loading.value = true; + error.value = ''; + const response = await axios.get('/columns', { + params: { tableName } + }); + if (response.data?.success) { + return { + success: true, + data: response.data.data, + message: '列配置加载成功' + }; + } + throw new Error(response.data?.message || '返回数据格式异常'); + } catch (err) { + error.value = `加载列配置失败: ${err.message}`; + console.error('加载列配置错误:', err); + return { + success: false, + data: [], + error: err.message + }; + } finally { + loading.value = false; + } +}; +export const getMainTable = async (tableName, pkColumnValue) => { + try { + loading.value = true; + error.value = ''; + + const params = { + tableName, + pkColumn: "b_id", + pkColumnValue + }; + + const response = await axios.get('/getobject', { params }); + + if (response.data?.success) { + return { + success: true, + data: { + current: { ...response.data.data }, + original: { ...response.data.data } + }, + message: '主表数据加载成功' + }; + } + throw new Error(response.data?.message || '返回数据格式异常'); + } catch (err) { + error.value = `加载主表数据失败: ${err.message}`; + console.error('加载主表错误:', err); + return { + success: false, + data: { + current: null, + original: null + }, + error: err.message + }; + } finally { + loading.value = false; + } +}; +/** + * 通用子表合并查询方法 + * @param {string} tableName 表名 + * @param {Ref} targetRef 目标数据的 ref + * @param {Ref} originalRef 原始数据的 ref(如果需要) + * @param {string} whereClause 条件 + * @param {function} transformFn 数据转换函数 (可选) + */ +export const getSubTableMerger = async ( + tableName, + targetRef, + originalRef, + whereClause, + orderby, + transformFn +) => { + try { + loading.value = true + error.value = null + + const params = { + tableName: tableName || 'v_bs_business_container', + pkColumn: orderby, + whereClause, + } + const response = await axios.get('/pagdetail', { params }) + + if (response.data?.success) { + // const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + + // // 去掉 subid 字段 + // const dataWithoutSubid = rawData.map(({ subid, ...rest }) => rest) + + // // 如果有 transformFn,就对数据做额外处理 + // const processedData = transformFn ? dataWithoutSubid.map(transformFn) : dataWithoutSubid + + + const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + + // 如果有 transformFn,就对数据做额外处理 + const processedData = transformFn ? rawData.map(transformFn) : rawData + + targetRef.value = [ + ...targetRef.value, + ...JSON.parse(JSON.stringify(processedData)) + ] + return processedData + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载数据失败: ${err.message}` + console.error('API Error:', err) + return [] + } finally { + loading.value = false + } +} + + +export const fetchTableColumnsEdit = async (tableName, targetRef) => { + try { + loading.value = true + error.value = null + + const response = await axios.get('/columnsEdit', { + params: { tableName }, + }) + if (response.data?.success) { + // 直接对传入的响应式引用赋值 + targetRef.value = response.data.data + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载列配置失败: ${err.message}` + // 出错时赋空数组 + targetRef.value = [] + } finally { + loading.value = false + } +} + + +export const getsubtable = async (tableName, targetRef, originalRef, whereClause) => { + try { + loading.value = true + error.value = null + + const params = { + tableName: tableName || 'v_bs_business_container', + pkColumn: 'subid', + whereClause, + } + const response = await axios.get('/pagdetail', { params }) + if (response.data?.success) { + const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + rawData.forEach(row => { + if (row.mx_sh !== undefined) { + row.mx_sh = Number(row.mx_sh) // 确保是 0 或 1 + } + if (row.mx_checkkp !== undefined) { + row.mx_checkkp = Number(row.mx_checkkp) // 确保是 0 或 1 + } + }) + + targetRef.value = JSON.parse(JSON.stringify(rawData)) + originalRef.value = JSON.parse(JSON.stringify(rawData)) + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载数据失败: ${err.message}` + return { data: [], total: 0 } + } finally { + loading.value = false + } +} + +// 获取主表数据 + + +export default { + getTableColumns, getMainTable, getSubTableMerger, fetchTableColumnsEdit, getsubtable +} + + +// 导出单例 diff --git a/code/link/link-frontend/src/utils/feeSummary.js b/code/link/link-frontend/src/utils/feeSummary.js new file mode 100644 index 00000000..bf4c9160 --- /dev/null +++ b/code/link/link-frontend/src/utils/feeSummary.js @@ -0,0 +1,50 @@ +// 工具函数:按币种汇总 +function sumByCurrency(data) { + const result = {} + data.forEach(row => { + result[row.mx_moneytype_id] = (result[row.mx_moneytype_id] || 0) + row.mx_amount_root + }) + return result +} + +// 工具函数:折合人民币 +function sumInRMB(data) { + return data.reduce((sum, row) => sum + row.mx_amount_root * row.mx_exrate, 0) +} + +// 主函数:计算应收、应付、利润 +export function calcFeeSummary(receivable = [], payable = []) { + console.log("进入======") + const receivableSummary = { + byCurrency: sumByCurrency(receivable), + rmb: sumInRMB(receivable) + } + + const payableSummary = { + byCurrency: sumByCurrency(payable), + rmb: sumInRMB(payable) + } + + // 计算利润(逐个币种相减 + RMB 总和) + const profitByCurrency = {} + const allCurrencies = new Set([ + ...Object.keys(receivableSummary.byCurrency), + ...Object.keys(payableSummary.byCurrency) + ]) + allCurrencies.forEach(cur => { + profitByCurrency[cur] = + (receivableSummary.byCurrency[cur] || 0) - + (payableSummary.byCurrency[cur] || 0) + }) + + const profitSummary = { + byCurrency: profitByCurrency, + rmb: receivableSummary.rmb - payableSummary.rmb + } + + return { + receivable: receivableSummary, + payable: payableSummary, + profit: profitSummary, + } +} diff --git a/code/link/link-frontend/src/utils/useTableHeaderSortable.js b/code/link/link-frontend/src/utils/useTableHeaderSortable.js new file mode 100644 index 00000000..32e5eb5d --- /dev/null +++ b/code/link/link-frontend/src/utils/useTableHeaderSortable.js @@ -0,0 +1,24 @@ +import Sortable from "sortablejs" + +export function useTableHeaderSortable(columnsRef, options = {}) { + const initSortable = () => { + const headerRow = document.querySelector(".el-table__header-wrapper tr") + if (!headerRow) return + + Sortable.create(headerRow, { + animation: 150, + handle: "th", + ...options, + onEnd(evt) { + const oldIndex = evt.oldIndex - 1 // 跳过 selection 列 + const newIndex = evt.newIndex - 1 + if (oldIndex >= 0 && newIndex >= 0) { + const moved = columnsRef.value.splice(oldIndex, 1)[0] + columnsRef.value.splice(newIndex, 0, moved) + } + } + }) + } + + return { initSortable } +} diff --git a/code/link/link-frontend/src/views/im/index.vue b/code/link/link-frontend/src/views/im/index.vue new file mode 100644 index 00000000..48798c9d --- /dev/null +++ b/code/link/link-frontend/src/views/im/index.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/code/link/link-frontend/test.html b/code/link/link-frontend/test.html new file mode 100644 index 00000000..361f58bd --- /dev/null +++ b/code/link/link-frontend/test.html @@ -0,0 +1,125 @@ + + + + + 列表示例 + + + + +

布局 1:时间在主信息下面

+
+
+
+
箱号:123456
+
完成时间:2025-10-11 14:30
+
+
+ 已完成 +
+
+
+ +

布局 2:时间在右侧 extra 区

+
+
+
箱号:123457
+
+
2025-10-11 15:00
+ 未完成 +
+
+
+ +

布局 3:时间与状态在同一行,右侧对齐

+
+
+
箱号:123458
+
+ 已完成 +
2025-10-11 16:00
+
+
+
+ +

布局 4:多行布局,箱号 + 时间在左,状态在右

+
+
+
+
箱号:123459
+
完成时间:2025-10-11 17:30
+
+
+ 未完成 +
+
+
+ + + diff --git a/code/link/link-fronted/vite.config.js b/code/link/link-frontend/vite.config.js similarity index 80% rename from code/link/link-fronted/vite.config.js rename to code/link/link-frontend/vite.config.js index f232b650..33920b27 100644 --- a/code/link/link-fronted/vite.config.js +++ b/code/link/link-frontend/vite.config.js @@ -7,7 +7,7 @@ export default defineConfig({ plugins: [vue()], resolve: { alias: { - '@': path.resolve(__dirname, 'src'), - }, + '@': path.resolve(__dirname, './src') + } }, })