diff --git a/.claude/memory/java-backend.md b/.claude/memory/java-backend.md new file mode 100644 index 0000000..f109f0c --- /dev/null +++ b/.claude/memory/java-backend.md @@ -0,0 +1,148 @@ +# Java 后端开发规范 + +本项目 Java 后端开发规范,基于 Spring Boot + MyBatis-Plus 技术栈。 + +## 核心原则 + +1. **OpenAPI 规范驱动** - 前后端通过接口规范对齐 +2. **类型安全优先** - 强制类型校验 +3. **约定大于配置** - 统一代码风格 +4. **自动化优先** - 能自动化的绝不手动 +5. **三层架构分离** - Controller、Service、Mapper 职责清晰 + +## 技术栈 + +| 组件 | 技术选型 | 版本 | +|------|---------|------| +| 框架 | Spring Boot | 3.2+ | +| 持久层 | MyBatis-Plus | 3.5+ | +| 对象映射 | MapStruct | 1.5+ | +| 数据库 | MySQL | 8.0+ | +| 缓存 | Redis | - | +| 安全 | Spring Security + JWT | - | +| API 文档 | Knife4j | 4.x | + +## 三层架构 + +| 层级 | 职责 | 数据接收 | 数据返回 | +|------|------|---------|---------| +| Controller | 接收请求、参数校验、返回响应 | DTO/Request | VO/Response | +| Service | 处理业务逻辑、事务控制 | DTO/Entity | Entity | +| Mapper | 数据库 CRUD 操作 | Entity/条件 | Entity | + +**核心规范:** +- Service↔Mapper 之间只用 Entity,禁止 DTO/VO 转换 +- 转换只在 Controller 层发生 +- Service 继承 `IService` +- 查询接口默认分页 + +## 消除魔法值规范 + +**禁止在代码中使用魔法值,所有状态、类型、常量必须使用枚举定义** + +- 枚举类存放在 `enums` 包下 +- 枚举包含 `code` 和 `desc` 字段 +- 提供 `getCode()`、`getDesc()`、`valueOfCode()` 方法 +- 数据库存储 `code`,代码中使用枚举 + +## ORM 实体类规范 + +### 表名命名 + +- `t_user_*` - 用户模块 +- `t_sys_*` - 系统模块 +- `t_biz_*` - 业务模块 +- `t_auth_*` - 权限模块 + +### 审计字段(必填) + +所有表必须包含审计字段: + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 主键(自增) | +| create_by | VARCHAR(50) | 创建人 | +| create_time | DATETIME | 创建时间 | +| update_by | VARCHAR(50) | 更新人 | +| update_time | DATETIME | 更新时间 | +| deleted | TINYINT | 逻辑删除(0-未删除,1-已删除) | + +- 审计字段通过 MyBatis-Plus `FieldFill` 自动填充 +- 禁止手动设置审计字段 + +## 日志规范 + +### 日志语言 + +**所有日志必须使用中文** + +### TraceId 链路追踪 + +- 使用 MDC 实现 TraceId 链路追踪 +- AOP 切面在请求入口生成 TraceId +- 日志格式包含 `[%X{traceId}]` + +### 环境差异化配置 + +| 环境 | 日志策略 | +|------|---------| +| 开发/测试 | 全量记录(DEBUG 级别) | +| 生产 | 精简记录(INFO/WARN 级别) | + +### AOP 日志实现 + +- 拦截 Controller 层所有方法 +- 请求日志:TraceId、请求方法、路径、IP、耗时 +- 异常日志:TraceId、异常类型、消息、堆栈 + +## 统一响应格式 + +```java +Result { + code: Integer; // 状态码 + message: String; // 消息 + data: T; // 数据 + timestamp: Long; // 时间戳 +} +``` + +**错误码:** 200-成功、400-参数错误、401-未授权、403-无权限、404-不存在、500-系统错误 + +## MapStruct 对象映射 + +- 使用 `@Mapper` 注解定义 Converter 接口 +- Entity ↔ VO 转换在 Controller 层调用 +- Service 层和 Mapper 层禁止 DTO/VO 转换 +- 命名规范:`XxxConverter` 或 `XxxMapStruct` + +## 工具类规范 + +- 工具函数集中管理,禁止在 Controller 层编写工具方法 +- 工具类私有构造、静态方法、无状态、线程安全 +- 命名规范:`XxxUtil`(通用)、`XxxHelper`(业务)、`XxxConverter`(转换) + +## 多环境配置 + +| 配置项 | dev | test | prod | +|--------|-----|------|------| +| SQL 日志 | 开启 | 开启 | 关闭 | +| Swagger | 开启 | 开启 | 关闭 | +| Flyway Clean | 允许 | 禁止 | 禁止 | + +## 快速参考 + +### Redis Key 命名 + +- `auth:token:{token}` - 用户 Token +- `user:info:{userId}` - 用户信息缓存 +- `dict:{type}` - 数据字典 +- `lock:{resource}:{id}` - 分布式锁 +- `rate_limit:{key}` - 限流计数器 + +### 核心环境变量 + +- `SPRING_PROFILES_ACTIVE` - 活跃环境 +- `DB_HOST`、`DB_PASSWORD` - 数据库配置 +- `REDIS_HOST`、`REDIS_PASSWORD` - Redis 配置 +- `JWT_SECRET` - JWT 密钥 +- `OSS_ACCESS_KEY_ID`、`OSS_ACCESS_KEY_SECRET` - OSS 配置 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..21ec934 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,202 @@ + +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 快速开始 + +```bash +# 安装所有依赖(前端 + 后端) +pnpm install + +# 同时启动前后端开发服务器 +pnpm dev + +# 或分别启动 +pnpm dev:frontend # 前端 http://localhost:3000 +pnpm dev:backend # 后端 http://localhost:3001 +``` + +## 技术栈 + +### 后端 +- **框架**: NestJS + TypeScript +- **数据库**: MySQL 8.0 + Prisma ORM +- **认证**: JWT + RBAC (基于角色的访问控制) +- **多租户**: 数据隔离架构(每个租户独立 tenantId) + +### 前端 +- **框架**: Vue 3 + TypeScript + Vite +- **UI 组件**: Ant Design Vue +- **状态管理**: Pinia +- **样式**: Tailwind CSS + SCSS + +## 核心命令 + +### 开发 +```bash +# 根目录 +pnpm dev # 同时启动前后端 +pnpm dev:frontend # 只启动前端 +pnpm dev:backend # 只启动后端 + +# 前端目录 +pnpm dev # 启动前端 + +# 后端目录 +pnpm start:dev # 启动后端 +pnpm prisma:studio # Prisma 数据库可视化 +``` + +### 数据库迁移 +```bash +cd backend +pnpm prisma:generate # 生成 Prisma Client +pnpm prisma:migrate # 开发环境迁移 +pnpm prisma:migrate:deploy # 生产环境部署 +``` + +### 构建 +```bash +pnpm build # 构建前后端 +pnpm build:frontend # 只构建前端 +pnpm build:backend # 只构建后端 +``` + +### 测试 +```bash +cd backend +pnpm test # 运行单元测试 +pnpm test:cov # 测试覆盖率 +``` + +## 架构概览 + +### 目录结构 +``` +library-picturebook-activity/ +├── backend/ # NestJS 后端 +│ ├── prisma/ # Prisma schema 和 migrations +│ ├── src/ +│ │ ├── auth/ # 认证模块 (JWT) +│ │ ├── users/ # 用户管理 +│ │ ├── roles/ # 角色权限 +│ │ ├── menus/ # 菜单管理 +│ │ ├── tenants/ # 租户管理 +│ │ ├── contests/ # 竞赛模块 +│ │ │ ├── contests/ # 竞赛管理 +│ │ │ ├── works/ # 作品管理 +│ │ │ ├── teams/ # 团队管理 +│ │ │ ├── registrations/ # 报名管理 +│ │ │ └── reviews/ # 评审管理 +│ │ ├── school/ # 学校模块 +│ │ │ ├── schools/ +│ │ │ ├── grades/ +│ │ │ ├── classes/ +│ │ │ ├── teachers/ +│ │ │ └── students/ +│ │ └── prisma/ # Prisma 服务 +│ └── package.json +│ +└── frontend/ # Vue 3 前端 + ├── src/ + │ ├── api/ # API 接口 + │ ├── views/ # 页面组件 + │ ├── components/ # 公共组件 + │ ├── stores/ # Pinia 状态 + │ ├── router/ # 路由配置 + │ └── composables/ # 组合式函数 + └── package.json +``` + +### 模块结构(后端) +每个功能模块包含: +- `module.ts` - 模块定义 +- `controller.ts` - REST 控制器 +- `service.ts` - 业务逻辑 +- `dto/` - 数据传输对象 + +### 多租户架构 +- 所有业务数据必须包含 `tenantId` 字段 +- 查询必须包含租户隔离条件 +- 超级租户(`isSuper = 1`)可访问所有数据 + +## 关键开发规范 + +### 后端规范 + +1. **租户隔离(强制)**:所有数据库查询必须包含 `tenantId` +```typescript +// ✅ 正确 +const data = await prisma.model.findMany({ where: { tenantId } }); + +// ❌ 错误 - 缺少 tenantId +const data = await prisma.model.findMany({}); +``` + +2. **审计字段**:所有表必须包含 + - `tenantId` - 租户 ID + - `creator`/`modifier` - 创建/修改人 + - `createTime`/`modifyTime` - 时间戳 + - `validState` - 有效状态(1-有效,2-失效) + +3. **权限控制**:使用 `@RequirePermission('module:action')` 装饰器 + +4. **DTO 验证**:使用 `class-validator` 装饰器 + +### 前端规范 + +1. **路由包含租户编码**:`/:tenantCode/xxx` + +2. **API 调用**:放在 `api/` 目录,按模块组织 + +3. **状态管理**:使用 Pinia,store 命名 `xxxStore` + +4. **组件语法**:使用 ` +``` + +### 3. Store 方法说明 + +#### `hasRole(role: string): boolean` + +检查用户是否有指定角色 + +```typescript +if (authStore.hasRole("super_admin")) { + // 用户是超级管理员 +} +``` + +#### `hasPermission(permission: string): boolean` + +检查用户是否有指定权限 + +```typescript +if (authStore.hasPermission("user:create")) { + // 用户可以创建用户 +} +``` + +#### `hasAnyRole(roles: string[]): boolean` + +检查用户是否有任一指定角色 + +```typescript +if (authStore.hasAnyRole(["admin", "editor"])) { + // 用户是 admin 或 editor +} +``` + +#### `hasAnyPermission(permissions: string[]): boolean` + +检查用户是否有任一指定权限 + +```typescript +if (authStore.hasAnyPermission(["user:create", "user:update"])) { + // 用户可以创建或更新用户 +} +``` + +## 🔄 工作流程 + +### 1. 应用启动流程 + +``` +应用启动 + ↓ +检查 localStorage 中是否有 token + ↓ +如果有 token,调用 fetchUserInfo() 获取用户信息 + ↓ +用户信息包含 roles 和 permissions + ↓ +应用挂载完成 +``` + +### 2. 路由导航流程 + +``` +用户访问路由 + ↓ +路由守卫 beforeEach + ↓ +检查 token 是否存在 + ↓ +如果 token 存在但 user 不存在 → 自动获取用户信息 + ↓ +检查 requiresAuth → 是否需要登录 + ↓ +检查 roles → 是否有指定角色 + ↓ +检查 permissions → 是否有指定权限 + ↓ +允许/拒绝访问 +``` + +### 3. 登录流程 + +``` +用户登录 + ↓ +调用 authApi.login() + ↓ +返回 token 和 user 信息(包含 roles 和 permissions) + ↓ +保存 token 到 localStorage + ↓ +保存 user 到 store + ↓ +跳转到目标页面 +``` + +## 📝 路由配置示例 + +### 完整的路由配置示例 + +```typescript +const routes: RouteRecordRaw[] = [ + { + path: "/login", + name: "Login", + component: () => import("@/views/auth/Login.vue"), + meta: { requiresAuth: false }, + }, + { + path: "/", + component: () => import("@/layouts/BasicLayout.vue"), + redirect: "/workbench", + meta: { requiresAuth: true }, + children: [ + { + path: "workbench", + name: "workbench", + component: () => import("@/views/workbench/Index.vue"), + meta: { + title: "仪表盘", + requiresAuth: true, + }, + }, + { + path: "system/users", + name: "SystemUsers", + component: () => import("@/views/system/users/Index.vue"), + meta: { + title: "用户管理", + requiresAuth: true, + permissions: ["user:read"], // 需要查看用户权限 + }, + }, + { + path: "system/roles", + name: "SystemRoles", + component: () => import("@/views/system/roles/Index.vue"), + meta: { + title: "角色管理", + requiresAuth: true, + roles: ["super_admin", "admin"], // 需要管理员角色 + }, + }, + ], + }, +]; +``` + +## 🎨 实际应用场景 + +### 场景 1: 根据权限显示菜单 + +```vue + +``` + +### 场景 2: 根据权限显示按钮 + +```vue + +``` + +### 场景 3: 组合权限检查 + +```vue + +``` + +## ⚠️ 注意事项 + +### 1. 路由守卫是异步的 + +路由守卫使用了 `async/await`,确保在检查权限前用户信息已加载。 + +### 2. 权限检查顺序 + +1. 认证检查(`requiresAuth`) +2. 角色检查(`roles`) +3. 权限检查(`permissions`) + +### 3. 403 页面 + +如果没有权限,会跳转到 `/403` 页面。你可以自定义这个页面。 + +### 4. 权限更新 + +如果用户权限发生变化,需要: + +- 重新登录,或 +- 调用 `authStore.fetchUserInfo()` 刷新用户信息 + +## 🔍 调试技巧 + +### 1. 查看当前用户信息 + +```typescript +import { useAuthStore } from "@/stores/auth"; + +const authStore = useAuthStore(); +console.log("用户信息:", authStore.user); +console.log("角色:", authStore.user?.roles); +console.log("权限:", authStore.user?.permissions); +``` + +### 2. 检查权限 + +```typescript +console.log("是否有 admin 角色:", authStore.hasRole("admin")); +console.log("是否有 user:create 权限:", authStore.hasPermission("user:create")); +``` + +## 📚 总结 + +现在权限控制系统已经完善: + +1. ✅ **自动获取用户信息** - 刷新页面不会丢失 +2. ✅ **路由权限检查** - 支持角色和权限控制 +3. ✅ **组件权限检查** - 可以在组件中使用权限方法 +4. ✅ **类型安全** - TypeScript 类型定义完善 + +可以开始使用权限控制功能了! diff --git a/java-frontend/TENANT_MANAGEMENT_GUIDE.md b/java-frontend/TENANT_MANAGEMENT_GUIDE.md new file mode 100644 index 0000000..2ac1d31 --- /dev/null +++ b/java-frontend/TENANT_MANAGEMENT_GUIDE.md @@ -0,0 +1,219 @@ +# 租户管理页面使用指南 + +## 概述 + +租户管理页面已创建完成,只有超级租户才能访问和使用。该页面提供了完整的租户管理功能,包括租户的创建、编辑、删除和菜单分配。 + +## 功能特性 + +### 1. 租户列表 + +- 显示所有租户的基本信息 +- 显示租户类型(超级租户/普通租户) +- 显示租户状态(有效/失效) +- 显示租户统计信息(用户数、角色数) +- 显示租户已分配的菜单 + +### 2. 创建租户 + +- 租户名称 +- 租户编码(用于访问链接,必须唯一) +- 租户域名(可选,用于子域名访问) +- 租户描述 + +### 3. 编辑租户 + +- 修改租户基本信息 +- 修改租户状态(有效/失效) +- 注意:租户编码创建后不可修改 + +### 4. 分配菜单 + +- 以树形结构展示所有可用菜单 +- 支持多选 +- 显示租户当前已分配的菜单 +- 可以批量分配或取消分配菜单 + +### 5. 删除租户 + +- 删除租户及其所有关联数据 +- 超级租户不能被删除 +- 删除操作不可恢复 + +## 权限要求 + +所有操作都需要相应的权限: + +- `tenant:create` - 创建租户 +- `tenant:read` - 查看租户列表和详情 +- `tenant:update` - 编辑租户和分配菜单 +- `tenant:delete` - 删除租户 + +**注意**:只有超级租户的用户才拥有这些权限。 + +## 添加租户管理菜单 + +### 方式一:通过数据库直接添加 + +在数据库中执行以下SQL,为超级租户添加租户管理菜单: + +```sql +-- 假设系统管理菜单的ID为某个值,需要根据实际情况调整 parent_id +-- 假设系统管理菜单的ID为 2(需要根据实际情况查询) + +INSERT INTO menus (name, path, icon, component, parent_id, permission, sort, valid_state, create_time, modify_time) +VALUES ( + '租户管理', + '/system/tenants', + 'TeamOutlined', + 'system/tenants/Index', + 2, -- 系统管理菜单的ID,需要根据实际情况调整 + 'tenant:read', + 7, -- 排序,放在其他系统管理菜单之后 + 1, + NOW(), + NOW() +); +``` + +### 方式二:通过后端API添加 + +使用超级管理员账号登录后,通过菜单管理接口添加: + +```bash +POST /api/menus +Authorization: Bearer +X-Tenant-Code: super + +{ + "name": "租户管理", + "path": "/system/tenants", + "icon": "TeamOutlined", + "component": "system/tenants/Index", + "parentId": 2, // 系统管理菜单的ID + "permission": "tenant:read", + "sort": 7 +} +``` + +### 方式三:更新初始化脚本 + +修改 `backend/scripts/init-menus.ts` 或 `backend/scripts/init-super-tenant.ts`,在菜单初始化时添加租户管理菜单。 + +## 页面访问 + +添加菜单后,超级租户的用户登录后可以在"系统管理"菜单下看到"租户管理"选项。 + +访问路径:`/system/tenants` + +## 使用示例 + +### 创建新租户 + +1. 点击"新增租户"按钮 +2. 填写租户信息: + - 租户名称:例如 "租户A" + - 租户编码:例如 "tenant-a"(必须唯一,只能包含小写字母、数字、下划线和连字符) + - 租户域名:例如 "tenant-a.example.com"(可选) + - 描述:租户的描述信息 +3. 点击"确定"创建 + +### 为租户分配菜单 + +1. 在租户列表中,点击某个租户的"分配菜单"按钮 +2. 在弹出的菜单树中,勾选要分配给该租户的菜单 +3. 点击"确定"保存 + +**注意**: + +- 只有被分配的菜单才会在该租户的用户登录后显示 +- 父菜单如果被分配,其子菜单也会自动显示(但需要单独分配才能访问) + +### 编辑租户 + +1. 点击租户列表中的"编辑"按钮 +2. 修改租户信息(租户编码不可修改) +3. 可以修改租户状态(有效/失效) +4. 点击"确定"保存 + +### 删除租户 + +1. 点击租户列表中的"删除"按钮 +2. 确认删除操作 +3. **警告**:删除租户会同时删除该租户的所有数据(用户、角色、权限等),此操作不可恢复 + +## 注意事项 + +1. **权限控制**:只有超级租户的用户才能看到和使用租户管理功能 +2. **租户编码唯一性**:租户编码必须全局唯一,创建后不可修改 +3. **超级租户保护**:超级租户不能被删除 +4. **菜单分配**:菜单分配后,租户的用户登录后才能看到相应的菜单 +5. **数据隔离**:每个租户的数据完全隔离,互不影响 + +## 故障排查 + +### 问题1:看不到租户管理菜单 + +**原因**:菜单未添加到数据库,或当前用户不是超级租户 + +**解决**: + +- 确认菜单已添加到数据库 +- 确认当前用户属于超级租户 +- 确认用户有 `tenant:read` 权限 +- 刷新页面或重新登录 + +### 问题2:无法创建租户 + +**原因**:缺少 `tenant:create` 权限 + +**解决**: + +- 确认当前用户有创建租户的权限 +- 联系超级管理员分配权限 + +### 问题3:菜单分配不生效 + +**原因**:菜单分配后,用户需要重新登录才能看到新菜单 + +**解决**: + +- 让租户的用户重新登录 +- 或者清除浏览器缓存后重新登录 + +## 技术实现 + +### 文件结构 + +``` +frontend/src/ +├── api/ +│ └── tenants.ts # 租户API接口 +├── views/ +│ └── system/ +│ └── tenants/ +│ └── Index.vue # 租户管理页面 +└── utils/ + └── menu.ts # 菜单工具(已添加租户管理组件映射) +``` + +### API接口 + +所有API接口都在 `frontend/src/api/tenants.ts` 中定义: + +- `getTenantsList()` - 获取租户列表 +- `getTenantDetail()` - 获取租户详情 +- `createTenant()` - 创建租户 +- `updateTenant()` - 更新租户 +- `deleteTenant()` - 删除租户 +- `getTenantMenus()` - 获取租户菜单树 + +### 组件映射 + +在 `frontend/src/utils/menu.ts` 中添加了组件映射: + +```typescript +"system/tenants/Index": () => import("@/views/system/tenants/Index.vue") +``` + +这样当菜单的 `component` 字段为 `system/tenants/Index` 时,系统会自动加载租户管理页面。 diff --git a/java-frontend/cmp-3d.conf b/java-frontend/cmp-3d.conf new file mode 100644 index 0000000..28c8f41 --- /dev/null +++ b/java-frontend/cmp-3d.conf @@ -0,0 +1,53 @@ +# ========== 活动管理系统 - cmp-3d.linkseaai.com ========== + +server { + listen 443 ssl; + server_name cmp-3d.linkseaai.com; + include /usr/local/nginx/conf/conf.d/linkseaai.ssl.conf; + root /data/apps/cmp-3d/; + include /usr/local/nginx/conf/conf.d/error.conf; + include /usr/local/nginx/conf/conf.d/static.conf; + + # ========== 超时配置 ========== + keepalive_timeout 300s; + send_timeout 180s; + proxy_connect_timeout 10s; + proxy_send_timeout 10s; + proxy_read_timeout 300s; + + # ========== 测试环境 - 前端 ========== + location /web-test/ { + root /data/apps/cmp-3d/; + index index.html index.htm; + try_files $uri $uri/ /web-test/index.html; + } + + # ========== 测试环境 - API 代理 ========== + location /api-test/ { + proxy_redirect off; + proxy_pass http://119.29.229.174:3234/api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # ========== 生产环境 - 前端 ========== + location /web/ { + root /data/apps/cmp-3d/; + index index.html index.htm; + try_files $uri $uri/ /web/index.html; + } + + # ========== 生产环境 - API 代理 ========== + location /api/ { + proxy_redirect off; + proxy_pass http://119.29.229.174:3234/api/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/java-frontend/competition-web-test-v1.0.0.tgz b/java-frontend/competition-web-test-v1.0.0.tgz new file mode 100644 index 0000000..9582c83 Binary files /dev/null and b/java-frontend/competition-web-test-v1.0.0.tgz differ diff --git a/java-frontend/docs/add-new-route.md b/java-frontend/docs/add-new-route.md new file mode 100644 index 0000000..746c544 --- /dev/null +++ b/java-frontend/docs/add-new-route.md @@ -0,0 +1,262 @@ +# 新增前端页面路由指南 + +本文档介绍如何在活动管理系统中新增前端页面路由。 + +## 概述 + +系统采用**动态路由**机制,路由配置存储在数据库中,前端根据用户权限动态加载。新增页面需要完成以下 4 个步骤: + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Step 1: 创建 Vue 组件文件 │ +│ frontend/src/views/xxx/Index.vue │ +├──────────────────────────────────────────────────────────────┤ +│ Step 2: 在 componentMap 中注册组件 │ +│ frontend/src/utils/menu.ts │ +├──────────────────────────────────────────────────────────────┤ +│ Step 3: 在数据库 menus 表中添加菜单记录 │ +├──────────────────────────────────────────────────────────────┤ +│ Step 4: 在数据库 tenant_menus 表中关联租户 │ +└──────────────────────────────────────────────────────────────┘ +``` + +## 详细步骤 + +### Step 1: 创建 Vue 组件文件 + +在 `frontend/src/views/` 目录下创建对应的 Vue 组件。 + +**目录结构规范:** +``` +frontend/src/views/ +├── workbench/ # 工作台模块 +│ └── Index.vue +├── contests/ # 活动管理模块 +│ ├── Index.vue # 活动列表 +│ ├── Create.vue # 创建活动 +│ ├── Detail.vue # 活动详情 +│ ├── registrations/ # 报名管理 +│ │ └── Index.vue +│ ├── works/ # 作品管理 +│ │ └── Index.vue +│ └── ... +├── system/ # 系统管理模块 +│ ├── users/ +│ │ └── Index.vue +│ ├── roles/ +│ │ └── Index.vue +│ └── ... +└── your-module/ # 你的新模块 + └── Index.vue +``` + +**组件模板示例:** +```vue + + + + + +``` + +### Step 2: 注册组件映射 + +在 `frontend/src/utils/menu.ts` 文件中的 `componentMap` 对象中添加组件映射。 + +**文件位置:** `frontend/src/utils/menu.ts` + +```typescript +const componentMap: Record Promise> = { + // 工作台 + "workbench/Index": () => import("@/views/workbench/Index.vue"), + + // 活动管理模块 + "contests/Index": () => import("@/views/contests/Index.vue"), + "contests/Create": () => import("@/views/contests/Create.vue"), + // ... + + // 系统管理模块 + "system/users/Index": () => import("@/views/system/users/Index.vue"), + // ... + + // ========== 新增组件映射 ========== + "your-module/Index": () => import("@/views/your-module/Index.vue"), + "your-module/Detail": () => import("@/views/your-module/Detail.vue"), +}; +``` + +**注意事项:** +- Key 格式:`模块名/组件名`,不需要 `@/views/` 前缀和 `.vue` 后缀 +- Value 格式:使用动态 import 函数 +- 如果未注册,控制台会输出警告信息 + +### Step 3: 添加数据库菜单记录 + +在数据库的 `menus` 表中插入菜单记录。 + +**menus 表结构:** +| 字段 | 类型 | 说明 | +|------|------|------| +| id | int | 主键,自增 | +| name | varchar | 菜单名称(显示文本) | +| path | varchar | 路由路径,如 `/contests` | +| component | varchar | 组件路径,对应 componentMap 的 key | +| icon | varchar | 图标名称,使用 Ant Design 图标 | +| parent_id | int | 父菜单ID,顶级菜单为 NULL | +| sort | int | 排序序号 | +| permission | varchar | 权限标识,如 `contest:read` | +| tenant_id | int | 租户ID | + +**SQL 示例:** + +```sql +-- 添加顶级菜单 +INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id) +VALUES ('新模块', '/your-module', 'your-module/Index', 'AppstoreOutlined', NULL, 10, 1); + +-- 获取刚插入的菜单ID +SET @parent_id = LAST_INSERT_ID(); + +-- 添加子菜单 +INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id) +VALUES ('子页面1', '/your-module/sub1', 'your-module/Sub1', NULL, @parent_id, 1, 1); + +INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id) +VALUES ('子页面2', '/your-module/sub2', 'your-module/Sub2', NULL, @parent_id, 2, 1); +``` + +**常用图标名称:** +- `HomeOutlined` - 首页 +- `AppstoreOutlined` - 应用 +- `TrophyOutlined` - 奖杯/活动 +- `TeamOutlined` - 团队 +- `UserOutlined` - 用户 +- `SettingOutlined` - 设置 +- `FileOutlined` - 文件 +- `FolderOutlined` - 文件夹 + +更多图标请参考:[Ant Design Icons](https://ant.design/components/icon-cn) + +### Step 4: 关联租户菜单 + +在 `tenant_menus` 表中添加租户与菜单的关联关系。 + +**SQL 示例:** + +```sql +-- 假设菜单ID为 30,租户ID为 1 +INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, 30); + +-- 如果刚插入菜单,可以使用 LAST_INSERT_ID() +INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, LAST_INSERT_ID()); + +-- 批量关联(关联多个租户) +INSERT INTO tenant_menus (tenant_id, menu_id) VALUES +(1, 30), +(2, 30), +(3, 30); +``` + +## 完整示例 + +假设要新增一个「公告管理」页面: + +### 1. 创建组件文件 + +**文件:** `frontend/src/views/announcements/Index.vue` + +```vue + + + +``` + +### 2. 注册组件 + +**文件:** `frontend/src/utils/menu.ts` + +```typescript +const componentMap: Record Promise> = { + // ... 其他组件 + "announcements/Index": () => import("@/views/announcements/Index.vue"), +}; +``` + +### 3. 添加菜单记录 + +```sql +INSERT INTO menus (name, path, component, icon, parent_id, sort, permission, tenant_id) +VALUES ('公告管理', '/announcements', 'announcements/Index', 'NotificationOutlined', NULL, 5, 'announcement:read', 1); +``` + +### 4. 关联租户 + +```sql +INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, LAST_INSERT_ID()); +``` + +### 5. 验证 + +1. 重新登录系统(清除缓存) +2. 检查侧边栏是否显示新菜单 +3. 点击菜单验证页面是否正常加载 + +## 常见问题 + +### Q1: 菜单不显示? + +检查以下几点: +1. `tenant_menus` 表是否已关联当前租户 +2. 用户角色是否有该菜单的权限 +3. 清除浏览器缓存后重新登录 + +### Q2: 点击菜单报 404? + +检查以下几点: +1. `componentMap` 是否已注册该组件 +2. 组件文件路径是否正确 +3. 查看控制台是否有警告信息 + +### Q3: 控制台警告「组件路径未在 componentMap 中定义」? + +在 `menu.ts` 的 `componentMap` 中添加对应的组件映射。 + +### Q4: 如何添加需要权限的页面? + +在 `menus` 表的 `permission` 字段设置权限标识,如 `announcement:read`,然后确保用户角色拥有该权限。 + +## 相关文件 + +| 文件 | 说明 | +|------|------| +| `frontend/src/utils/menu.ts` | 组件映射、菜单转换工具 | +| `frontend/src/router/index.ts` | 路由配置、动态路由加载 | +| `frontend/src/stores/auth.ts` | 用户认证、菜单数据存储 | +| `frontend/src/layouts/BasicLayout.vue` | 主布局、侧边栏菜单渲染 | diff --git a/java-frontend/docs/theme-customization.md b/java-frontend/docs/theme-customization.md new file mode 100644 index 0000000..074a446 --- /dev/null +++ b/java-frontend/docs/theme-customization.md @@ -0,0 +1,226 @@ +# 主题色修改指南 + +本文档说明如何修改 Ant Design Vue 的主题色。 + +## 📋 概述 + +项目使用 Ant Design Vue 4.x 作为 UI 组件库,主题色配置主要通过两个文件实现: + +1. **`src/styles/theme.scss`** - CSS 变量定义,用于全局样式和侧边栏等自定义组件 +2. **`src/App.vue`** - Ant Design Vue 的 ConfigProvider 配置,用于组件库的主题色 + +## 📁 需要修改的文件 + +### 1. `src/styles/theme.scss` + +该文件定义了 CSS 变量,用于: + +- 全局主题色变量 +- 侧边栏菜单样式 +- 自定义组件的主题色 + +### 2. `src/App.vue` + +该文件通过 `a-config-provider` 组件配置 Ant Design Vue 的主题色,影响所有 Ant Design Vue 组件的默认颜色。 + +## 🔧 修改步骤 + +### 步骤 1: 修改 `src/styles/theme.scss` + +在 `:root` 选择器中修改以下 CSS 变量: + +```scss +:root { + // 主色调 + --ant-color-primary: #1890ff; // 主色 + --ant-color-primary-hover: #40a9ff; // 悬停色(通常比主色浅) + --ant-color-primary-active: #096dd9; // 激活色(通常比主色深) + --ant-color-primary-bg: #e6f7ff; // 主色背景(浅色背景) + --ant-color-primary-bg-hover: #bae7ff; // 主色背景悬停 + + // 信息色(通常与主色一致) + --ant-color-info: #1890ff; + --ant-color-info-bg: #e6f7ff; + + // 链接色(通常与主色一致) + --ant-color-link: #1890ff; + --ant-color-link-hover: #40a9ff; + --ant-color-link-active: #096dd9; + + // 侧边栏相关颜色 + --sidebar-menu-item-hover: #e6f7ff; // 菜单项悬停背景 + --sidebar-menu-item-selected-bg: #e6f7ff; // 菜单项选中背景色 + --sidebar-menu-text-selected: #1890ff; // 选中菜单文字颜色 +} +``` + +### 步骤 2: 修改 `src/App.vue` + +在 `themeConfig` 对象中修改主题色配置: + +```typescript +const themeConfig: ConfigProviderProps["theme"] = { + token: { + colorPrimary: "#1890ff", // 主色调 + colorInfo: "#1890ff", // 信息色(通常与主色一致) + colorLink: "#1890ff", // 链接色(通常与主色一致) + borderRadius: 6, // 圆角(可选) + }, + algorithm: undefined, // 使用默认算法 +} +``` + +## 🎨 颜色值说明 + +### 主色调(Primary) + +- **主色(colorPrimary)**: 按钮、链接、选中状态等的主要颜色 +- **悬停色(hover)**: 鼠标悬停时的颜色,通常比主色浅 10-20% +- **激活色(active)**: 点击时的颜色,通常比主色深 10-20% +- **背景色(bg)**: 选中项的背景色,通常是主色的 5-10% 透明度 + +### 颜色搭配建议 + +1. **悬停色**: 在主色的基础上增加亮度(HSL 的 L 值增加 10-15%) +2. **激活色**: 在主色的基础上降低亮度(HSL 的 L 值减少 10-15%) +3. **背景色**: 使用主色的浅色版本(透明度约 5-10%) + +## 📝 常见主题色示例 + +### 拂晓蓝主题(Daybreak Blue)- 官方推荐 + +**拂晓蓝**是 Ant Design 官方的基础色板中的蓝色,代表"包容、科技、普惠"。这是 Ant Design Vue 的默认主题色。 + +参考文档:[Ant Design 色彩规范](https://ant.design/docs/spec/colors-cn#%E7%B3%BB%E7%BB%9F%E7%BA%A7%E8%89%B2%E5%BD%A9%E4%BD%93%E7%B3%BB) + +```scss +// theme.scss +// 拂晓蓝色板:blue-0 (#E6F4FF) 到 blue-9 (#001D66) +--ant-color-primary: #1890ff; // 主色 - blue-5(Ant Design Vue 4.x) +--ant-color-primary-hover: #40a9ff; // 悬停色 - blue-4 +--ant-color-primary-active: #096dd9; // 激活色 - blue-6 +--ant-color-primary-bg: #e6f7ff; // 主色背景 - blue-0(最浅) +``` + +```typescript +// App.vue +colorPrimary: "#1890ff" // 拂晓蓝主色 +``` + +**完整的拂晓蓝色板**: + +- blue-0: `#E6F4FF` (最浅) +- blue-1: `#BAE0FF` +- blue-2: `#91CAFF` +- blue-3: `#69B1FF` +- blue-4: `#4096FF` +- blue-5: `#1890ff` (主色,Ant Design Vue 4.x) +- blue-6: `#0958D9` +- blue-7: `#003EB3` +- blue-8: `#002C8C` +- blue-9: `#001D66` (最深) + +### 橙色主题 + +```scss +// theme.scss +--ant-color-primary: #ff7a00; +--ant-color-primary-hover: #ff9a2e; +--ant-color-primary-active: #d46b08; +--ant-color-primary-bg: #fff7e6; +``` + +```typescript +// App.vue +colorPrimary: "#ff7a00" +``` + +### 墨绿色主题 + +```scss +// theme.scss +--ant-color-primary: #01412b; +--ant-color-primary-hover: #026b47; +--ant-color-primary-active: #013320; +--ant-color-primary-bg: #e2f0ed; +``` + +```typescript +// App.vue +colorPrimary: "#01412b" +``` + +### 紫色主题 + +```scss +// theme.scss +--ant-color-primary: #722ed1; +--ant-color-primary-hover: #9254de; +--ant-color-primary-active: #531dab; +--ant-color-primary-bg: #f9f0ff; +``` + +```typescript +// App.vue +colorPrimary: "#722ed1" +``` + +### 红色主题 + +```scss +// theme.scss +--ant-color-primary: #f5222d; +--ant-color-primary-hover: #ff4d4f; +--ant-color-primary-active: #cf1322; +--ant-color-primary-bg: #fff1f0; +``` + +```typescript +// App.vue +colorPrimary: "#f5222d" +``` + +## ⚠️ 注意事项 + +1. **同步修改**: 必须同时修改 `theme.scss` 和 `App.vue` 两个文件,确保主题色一致 + +2. **颜色对比度**: 确保文字颜色与背景色有足够的对比度,符合无障碍访问标准(WCAG AA 级别) + +3. **侧边栏颜色**: 如果修改了主色调,记得同步更新侧边栏相关的 CSS 变量: + - `--sidebar-menu-item-hover` + - `--sidebar-menu-item-selected-bg` + - `--sidebar-menu-text-selected` + +4. **深色模式**: 如果项目支持深色模式,需要在 `[data-theme="dark"]` 选择器中定义深色模式的主题色 + +5. **浏览器缓存**: 修改后可能需要清除浏览器缓存或强制刷新(Ctrl+F5)才能看到效果 + +6. **颜色格式**: 使用十六进制颜色值(如 `#1890ff`),也可以使用 RGB/RGBA 格式 + +## 🔍 颜色工具推荐 + +- **Ant Design 色彩工具**: https://ant.design/docs/spec/colors-cn +- **Coolors**: https://coolors.co/ - 配色方案生成器 +- **Adobe Color**: https://color.adobe.com/ - 颜色搭配工具 +- **Material Design Color Tool**: https://material.io/resources/color/ - Material Design 配色工具 + +## 📚 相关文档 + +- [Ant Design Vue 主题定制](https://antdv.com/docs/vue/customize-theme-cn) +- [CSS 变量](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties) + +## 🎯 快速修改模板 + +如果需要快速修改主题色,可以按照以下模板操作: + +1. 选择主色调(例如:`#ff7a00`) +2. 计算悬停色(主色 + 亮度) +3. 计算激活色(主色 - 亮度) +4. 选择背景色(主色的浅色版本) + +然后替换以下位置的值: + +- `src/styles/theme.scss` 中的 `--ant-color-primary*` 变量 +- `src/App.vue` 中的 `colorPrimary`、`colorInfo`、`colorLink` 值 + +修改完成后,重启开发服务器或刷新页面即可看到效果。 diff --git a/java-frontend/index.html b/java-frontend/index.html new file mode 100644 index 0000000..45e2303 --- /dev/null +++ b/java-frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + 乐绘世界创想活动乐园 + + +
+ + + diff --git a/java-frontend/package-lock.json b/java-frontend/package-lock.json new file mode 100644 index 0000000..4139e45 --- /dev/null +++ b/java-frontend/package-lock.json @@ -0,0 +1,5756 @@ +{ + "name": "competition-management-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "competition-management-frontend", + "version": "1.0.0", + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@vee-validate/zod": "^4.12.4", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "ant-design-vue": "^4.1.1", + "axios": "^1.6.7", + "dayjs": "^1.11.10", + "pinia": "^2.1.7", + "three": "^0.182.0", + "vee-validate": "^4.12.4", + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/multer": "^2.0.0", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/eslint-config-typescript": "^13.0.0", + "autoprefixer": "^10.4.18", + "eslint": "^8.57.0", + "eslint-plugin-vue": "^9.22.0", + "postcss": "^8.4.35", + "sass": "^1.71.1", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.3", + "vite": "^5.1.6", + "vue-tsc": "^1.8.27" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/icons-vue": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz", + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1" + }, + "peerDependencies": { + "vue": ">=3.0.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@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.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "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/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "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/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@simonwep/pickr": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz", + "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", + "license": "MIT", + "dependencies": { + "core-js": "^3.15.1", + "nanopop": "^2.1.0" + } + }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", + "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==", + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@types/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.8.tgz", + "integrity": "sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@uppy/companion-client": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.2.tgz", + "integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==", + "license": "MIT", + "dependencies": { + "@uppy/utils": "^4.1.2", + "namespace-emitter": "^2.0.1" + } + }, + "node_modules/@uppy/core": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.4.tgz", + "integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==", + "license": "MIT", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/store-default": "^2.1.1", + "@uppy/utils": "^4.1.3", + "lodash.throttle": "^4.1.1", + "mime-match": "^1.0.2", + "namespace-emitter": "^2.0.1", + "nanoid": "^3.1.25", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/store-default": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.1.tgz", + "integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==", + "license": "MIT" + }, + "node_modules/@uppy/utils": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==", + "license": "MIT", + "dependencies": { + "lodash.throttle": "^4.1.1" + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz", + "integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==", + "license": "MIT", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2", + "nanoid": "^3.1.25" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@vee-validate/zod": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@vee-validate/zod/-/zod-4.15.1.tgz", + "integrity": "sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==", + "license": "MIT", + "dependencies": { + "type-fest": "^4.8.3", + "vee-validate": "4.15.1" + }, + "peerDependencies": { + "zod": "^3.24.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@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/@volar/language-core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", + "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "1.11.1" + } + }, + "node_modules/@volar/source-map": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", + "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "muggle-string": "^0.3.1" + } + }, + "node_modules/@volar/typescript": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", + "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "1.11.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", + "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.26", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz", + "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz", + "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.26", + "@vue/compiler-dom": "3.5.26", + "@vue/compiler-ssr": "3.5.26", + "@vue/shared": "3.5.26", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", + "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "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.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-13.0.0.tgz", + "integrity": "sha512-MHh9SncG/sfqjVqjcuFLOLD6Ed4dRAis4HNt0dXASeAuLqIAx4YMB1/m2o4pUKK1vCt8fUvYG8KKX2Ot3BVZTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "vue-eslint-parser": "^9.3.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "peerDependencies": { + "eslint": "^8.56.0", + "eslint-plugin-vue": "^9.0.0", + "typescript": ">=4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~1.11.1", + "@volar/source-map": "~1.11.1", + "@vue/compiler-dom": "^3.3.0", + "@vue/shared": "^3.3.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.3.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz", + "integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz", + "integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz", + "integrity": "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.26", + "@vue/runtime-core": "3.5.26", + "@vue/shared": "3.5.26", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz", + "integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.26", + "@vue/shared": "3.5.26" + }, + "peerDependencies": { + "vue": "3.5.26" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "license": "MIT" + }, + "node_modules/@wangeditor/basic-modules": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz", + "integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==", + "license": "MIT", + "dependencies": { + "is-url": "^1.2.4" + }, + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.throttle": "^4.1.1", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/code-highlight": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz", + "integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.23.0" + }, + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/core": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.19.tgz", + "integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==", + "license": "MIT", + "dependencies": { + "@types/event-emitter": "^0.3.3", + "event-emitter": "^0.3.5", + "html-void-elements": "^2.0.0", + "i18next": "^20.4.0", + "scroll-into-view-if-needed": "^2.2.28", + "slate-history": "^0.66.0" + }, + "peerDependencies": { + "@uppy/core": "^2.1.1", + "@uppy/xhr-upload": "^2.0.3", + "dom7": "^3.0.0", + "is-hotkey": "^0.2.0", + "lodash.camelcase": "^4.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "lodash.foreach": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "lodash.toarray": "^4.4.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/editor": { + "version": "5.1.23", + "resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.23.tgz", + "integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==", + "license": "MIT", + "dependencies": { + "@uppy/core": "^2.1.1", + "@uppy/xhr-upload": "^2.0.3", + "@wangeditor/basic-modules": "^1.1.7", + "@wangeditor/code-highlight": "^1.0.3", + "@wangeditor/core": "^1.1.19", + "@wangeditor/list-module": "^1.0.5", + "@wangeditor/table-module": "^1.1.4", + "@wangeditor/upload-image-module": "^1.0.2", + "@wangeditor/video-module": "^1.1.4", + "dom7": "^3.0.0", + "is-hotkey": "^0.2.0", + "lodash.camelcase": "^4.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "lodash.foreach": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "lodash.toarray": "^4.4.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/editor-for-vue": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz", + "integrity": "sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/editor": ">=5.1.0", + "vue": "^3.0.5" + } + }, + "node_modules/@wangeditor/list-module": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.5.tgz", + "integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/table-module": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.4.tgz", + "integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/upload-image-module": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz", + "integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==", + "license": "MIT", + "peerDependencies": { + "@uppy/core": "^2.0.3", + "@uppy/xhr-upload": "^2.0.3", + "@wangeditor/basic-modules": "1.x", + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.foreach": "^4.5.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/video-module": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.4.tgz", + "integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==", + "license": "MIT", + "peerDependencies": { + "@uppy/core": "^2.1.4", + "@uppy/xhr-upload": "^2.0.7", + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ant-design-vue": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz", + "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-vue": "^7.0.0", + "@babel/runtime": "^7.10.5", + "@ctrl/tinycolor": "^3.5.0", + "@emotion/hash": "^0.9.0", + "@emotion/unitless": "^0.8.0", + "@simonwep/pickr": "~1.8.0", + "array-tree-filter": "^2.1.0", + "async-validator": "^4.0.0", + "csstype": "^3.1.1", + "dayjs": "^1.10.5", + "dom-align": "^1.12.1", + "dom-scroll-into-view": "^2.0.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.15", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.25", + "shallow-equal": "^1.0.0", + "stylis": "^4.1.3", + "throttle-debounce": "^5.0.0", + "vue-types": "^3.0.0", + "warning": "^4.0.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design-vue" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/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/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/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.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "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.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "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.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/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.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/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.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "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.npmjs.org/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.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001762", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", + "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", + "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.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-align": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==", + "license": "MIT" + }, + "node_modules/dom-scroll-into-view": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz", + "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==", + "license": "MIT" + }, + "node_modules/dom7": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz", + "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", + "license": "MIT", + "dependencies": { + "ssr-window": "^3.0.0-alpha.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/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.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", + "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "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.npmjs.org/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.npmjs.org/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.npmjs.org/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.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/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.", + "dev": true, + "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-plugin-vue": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/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.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "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.npmjs.org/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.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "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.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "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": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/i18next": { + "version": "20.6.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz", + "integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/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.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hotkey": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz", + "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "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.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", + "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "license": "ISC", + "dependencies": { + "wildcard": "^1.1.0" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/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.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/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/nanopop": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.4.2.tgz", + "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/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/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "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" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.28.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz", + "integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sass": { + "version": "1.97.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.1.tgz", + "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slate": { + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz", + "integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.6", + "is-plain-object": "^5.0.0", + "tiny-warning": "^1.0.3" + } + }, + "node_modules/slate-history": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz", + "integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^5.0.0" + }, + "peerDependencies": { + "slate": ">=0.65.3" + } + }, + "node_modules/slate-history/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/slate/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snabbdom": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.6.3.tgz", + "integrity": "sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==", + "license": "MIT", + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/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.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssr-window": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", + "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", + "license": "MIT" + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/three": { + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", + "license": "MIT" + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/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/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vee-validate": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.15.1.tgz", + "integrity": "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.5.2", + "type-fest": "^4.8.3" + }, + "peerDependencies": { + "vue": "^3.4.26" + } + }, + "node_modules/vee-validate/node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz", + "integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.26", + "@vue/compiler-sfc": "3.5.26", + "@vue/runtime-dom": "3.5.26", + "@vue/server-renderer": "3.5.26", + "@vue/shared": "3.5.26" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~1.11.1", + "@vue/language-core": "1.8.27", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/vue-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz", + "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==", + "license": "MIT", + "dependencies": { + "is-plain-object": "3.0.1" + }, + "engines": { + "node": ">=10.15.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==", + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/java-frontend/package.json b/java-frontend/package.json new file mode 100644 index 0000000..6bb3db2 --- /dev/null +++ b/java-frontend/package.json @@ -0,0 +1,44 @@ +{ + "name": "competition-management-frontend", + "version": "1.0.0", + "type": "module", + "scripts": { + "compress:test": "node scripts/compress.cjs test", + "compress:prod": "node scripts/compress.cjs production", + "dev": "vite", + "build": "vue-tsc -b && vite build", + "build:test": "vite build --mode test", + "build:prod": "vite build --mode production", + "preview": "vite preview", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" + }, + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@vee-validate/zod": "^4.12.4", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "ant-design-vue": "^4.1.1", + "axios": "^1.6.7", + "dayjs": "^1.11.10", + "pinia": "^2.1.7", + "three": "^0.182.0", + "vee-validate": "^4.12.4", + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/multer": "^2.0.0", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/eslint-config-typescript": "^13.0.0", + "autoprefixer": "^10.4.18", + "eslint": "^8.57.0", + "eslint-plugin-vue": "^9.22.0", + "postcss": "^8.4.35", + "sass": "^1.71.1", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.3", + "vite": "^5.1.6", + "vue-tsc": "^3.2.2" + } +} diff --git a/java-frontend/postcss.config.js b/java-frontend/postcss.config.js new file mode 100644 index 0000000..b4a6220 --- /dev/null +++ b/java-frontend/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} + diff --git a/java-frontend/scripts/compress.cjs b/java-frontend/scripts/compress.cjs new file mode 100644 index 0000000..e3d98bf --- /dev/null +++ b/java-frontend/scripts/compress.cjs @@ -0,0 +1,116 @@ +const { execSync } = require("child_process") +const path = require("path") +const fs = require("fs") + +/** + * 压缩前端打包文件 + * 压缩包放在根目录下,文件名格式: competition-web-{env}-v{version}.tgz + * + * 用法: + * node scripts/compress.cjs test - 测试环境 + * node scripts/compress.cjs production - 生产环境 + */ +function compressFrontend() { + const rootDir = path.join(__dirname, "..") + const sourceDir = path.join(rootDir, "dist") + const outputDir = rootDir + + // 获取环境参数 + const env = process.argv[2] + if (!env || !["test", "production"].includes(env)) { + console.error("❌ 错误: 请指定环境参数") + console.error(" 用法: node scripts/compress.cjs ") + console.error(" 示例: node scripts/compress.cjs test") + console.error(" 示例: node scripts/compress.cjs production") + process.exit(1) + } + + // 从 package.json 读取版本号 + const packageJsonPath = path.join(rootDir, "package.json") + let version = "1.0.0" + try { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) + version = packageJson.version || "1.0.0" + } catch (error) { + console.warn(`⚠️ 无法读取 package.json 版本号,使用默认版本: ${version}`) + } + + // 检查源目录是否存在 + if (!fs.existsSync(sourceDir)) { + console.error("❌ 错误: 前端打包文件不存在") + console.error(` 路径: ${sourceDir}`) + console.error( + ` 请先运行 pnpm build:${env === "test" ? "test" : ""} 构建前端项目`, + ) + process.exit(1) + } + + // 删除之前的所有压缩包 + console.log("🧹 清理旧的压缩包...\n") + try { + const files = fs.readdirSync(outputDir) + const oldZipFiles = files.filter( + (file) => file.startsWith("competition-web-") && file.endsWith(".tgz"), + ) + + if (oldZipFiles.length > 0) { + oldZipFiles.forEach((file) => { + const filePath = path.join(outputDir, file) + try { + fs.unlinkSync(filePath) + console.log(` ✅ 已删除: ${file}`) + } catch (error) { + console.warn(` ⚠️ 删除失败: ${file} - ${error.message}`) + } + }) + console.log("") + } else { + console.log(" ℹ️ 没有找到旧的压缩包\n") + } + } catch (error) { + console.warn(` ⚠️ 清理旧压缩包时出错: ${error.message}\n`) + } + + // 生成文件名: competition-web-{env}-v{version}.tgz + const zipFileName = `competition-web-${env}-v${version}.tgz` + const zipFilePath = path.join(outputDir, zipFileName) + + console.log("📦 开始压缩前端打包文件...\n") + console.log(` 环境: ${env}`) + console.log(` 版本: v${version}`) + console.log(` 源目录: ${sourceDir}`) + console.log(` 输出文件: ${zipFilePath}\n`) + + try { + // 使用相对路径,避免 Windows tar 路径问题 + const tarCommand = `tar -czf "${zipFileName}" -C dist .` + + execSync(tarCommand, { + cwd: rootDir, + stdio: "inherit", + shell: true, + env: { ...process.env }, + }) + + // 检查文件是否创建成功 + if (fs.existsSync(zipFilePath)) { + const stats = fs.statSync(zipFilePath) + const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(2) + console.log(`\n✅ 压缩完成!`) + console.log(` 文件: ${zipFileName}`) + console.log(` 大小: ${fileSizeMB} MB`) + console.log(` 路径: ${zipFilePath}`) + } else { + throw new Error("压缩文件未生成") + } + } catch (error) { + console.error("\n❌ 压缩失败:", error.message) + console.error("\n提示:") + console.error(" 1. 确保已安装tar命令 (Windows 10+内置支持)") + console.error(" 2. 确保有足够的磁盘空间") + console.error(" 3. 确保输出目录有写入权限") + process.exit(1) + } +} + +compressFrontend() diff --git a/java-frontend/src/App.vue b/java-frontend/src/App.vue new file mode 100644 index 0000000..86a0a9a --- /dev/null +++ b/java-frontend/src/App.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/java-frontend/src/api/ai-3d.ts b/java-frontend/src/api/ai-3d.ts new file mode 100644 index 0000000..7edf639 --- /dev/null +++ b/java-frontend/src/api/ai-3d.ts @@ -0,0 +1,119 @@ +import request from "@/utils/request"; +import type { PaginationParams } from "@/types/api"; + +// ==================== AI 3D 任务相关类型 ==================== + +/** + * AI 3D 任务状态 + */ +export type AI3DTaskStatus = + | "pending" + | "processing" + | "completed" + | "failed" + | "timeout"; + +/** + * AI 3D 任务输入类型 + */ +export type AI3DInputType = "text" | "image"; + +/** + * AI 3D 任务 + */ +export interface AI3DTask { + id: number; + tenantId: number; + userId: number; + inputType: AI3DInputType; + inputContent: string; + status: AI3DTaskStatus; + resultUrl?: string; + previewUrl?: string; + // 多结果支持(文生3D会生成4个不同角度的模型) + resultUrls?: string[]; + previewUrls?: string[]; + errorMessage?: string; + externalTaskId?: string; + retryCount: number; + createTime: string; + completeTime?: string; + // 队列位置(仅 pending 状态时返回) + queuePosition?: number; +} + +/** + * 模型生成类型 + */ +export type AI3DGenerateType = "Normal" | "LowPoly" | "Geometry" | "Sketch"; + +/** + * 创建任务参数 + */ +export interface CreateAI3DTaskParams { + inputType: AI3DInputType; + inputContent: string; + /** 模型生成类型:Normal-带纹理, LowPoly-低多边形, Geometry-白模, Sketch-草图 */ + generateType?: AI3DGenerateType; + /** 模型面数:10000-1500000,默认500000 */ + faceCount?: number; +} + +/** + * 查询任务参数 + */ +export interface QueryAI3DTaskParams extends PaginationParams { + status?: AI3DTaskStatus; +} + +/** + * 任务列表响应 + */ +export interface AI3DTaskListResponse { + list: AI3DTask[]; + total: number; + page: number; + pageSize: number; +} + +// ==================== API 接口 ==================== + +/** + * 创建生成任务 + * POST /api/ai-3d/generate + */ +export function createAI3DTask(data: CreateAI3DTaskParams) { + return request.post("/ai-3d/generate", data); +} + +/** + * 获取任务列表 + * GET /api/ai-3d/tasks + */ +export function getAI3DTasks(params?: QueryAI3DTaskParams) { + return request.get("/ai-3d/tasks", { params }); +} + +/** + * 获取任务详情 + * GET /api/ai-3d/tasks/:id + */ +export function getAI3DTask(id: number) { + return request.get(`/ai-3d/tasks/${id}`); +} + +/** + * 重试任务 + * POST /api/ai-3d/tasks/:id/retry + */ +export function retryAI3DTask(id: number) { + return request.post(`/ai-3d/tasks/${id}/retry`); +} + +/** + * 删除任务 + * DELETE /api/ai-3d/tasks/:id + */ +export function deleteAI3DTask(id: number) { + return request.delete(`/ai-3d/tasks/${id}`); +} diff --git a/java-frontend/src/api/auth.ts b/java-frontend/src/api/auth.ts new file mode 100644 index 0000000..ba52aa1 --- /dev/null +++ b/java-frontend/src/api/auth.ts @@ -0,0 +1,23 @@ +import request from "@/utils/request"; +import type { LoginForm, LoginResponse, User } from "@/types/auth"; + +export const authApi = { + login: async (data: LoginForm): Promise => { + const response = await request.post("/auth/login", data); + return response as unknown as LoginResponse; + }, + + logout: async (): Promise => { + await request.post("/auth/logout"); + }, + + getUserInfo: async (): Promise => { + const response = await request.get("/auth/user-info"); + return response as unknown as User; + }, + + refreshToken: async (): Promise<{ token: string }> => { + const response = await request.post("/auth/refresh-token"); + return response as unknown as { token: string }; + }, +}; diff --git a/java-frontend/src/api/classes.ts b/java-frontend/src/api/classes.ts new file mode 100644 index 0000000..2550659 --- /dev/null +++ b/java-frontend/src/api/classes.ts @@ -0,0 +1,91 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Class { + id: number; + tenantId: number; + gradeId: number; + name: string; + code: string; + type: number; // 1-行政班级,2-兴趣班 + capacity?: number; + description?: string; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + grade?: { + id: number; + name: string; + code: string; + level: number; + }; + _count?: { + students: number; + studentInterestClasses: number; + }; +} + +export interface CreateClassForm { + gradeId: number; + name: string; + code: string; + type: number; + capacity?: number; + description?: string; +} + +export interface UpdateClassForm { + gradeId?: number; + name?: string; + code?: string; + type?: number; + capacity?: number; + description?: string; +} + +// 获取班级列表 +export async function getClassesList( + params: PaginationParams & { gradeId?: number; type?: number } +): Promise> { + const response = await request.get>("/classes", { + params, + }); + return response; +} + +// 获取单个班级详情 +export async function getClassDetail(id: number): Promise { + const response = await request.get(`/classes/${id}`); + return response; +} + +// 创建班级 +export async function createClass(data: CreateClassForm): Promise { + const response = await request.post("/classes", data); + return response; +} + +// 更新班级 +export async function updateClass( + id: number, + data: UpdateClassForm +): Promise { + const response = await request.patch(`/classes/${id}`, data); + return response; +} + +// 删除班级 +export async function deleteClass(id: number): Promise { + return await request.delete(`/classes/${id}`); +} + +export const classesApi = { + getList: getClassesList, + getDetail: getClassDetail, + create: createClass, + update: updateClass, + delete: deleteClass, +}; + diff --git a/java-frontend/src/api/config.ts b/java-frontend/src/api/config.ts new file mode 100644 index 0000000..e7993b9 --- /dev/null +++ b/java-frontend/src/api/config.ts @@ -0,0 +1,88 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Config { + id: number; + key: string; + value: string; + description?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; +} + +export interface CreateConfigForm { + key: string; + value: string; + description?: string; +} + +export interface UpdateConfigForm { + key?: string; + value?: string; + description?: string; +} + +// 获取配置列表 +export async function getConfigsList( + params: PaginationParams +): Promise> { + const response = await request.get>( + "/sys-config/page", + { + params, + } + ); + return response; +} + +// 获取单个配置详情 +export async function getConfigDetail(id: number): Promise { + const response = await request.get(`/sys-config/${id}`); + return response; +} + +// 根据key获取配置 +export async function getConfigByKey(key: string): Promise { + const response = await request.get(`/sys-config/key/${key}`); + return response; +} + +// 创建配置 +export async function createConfig(data: CreateConfigForm): Promise { + const response = await request.postForm("/sys-config", { + configKey: data.key, + configValue: data.value, + description: data.description, + }); + return response; +} + +// 更新配置 +export async function updateConfig( + id: number, + data: UpdateConfigForm +): Promise { + const response = await request.putForm(`/sys-config/${id}`, { + configValue: data.value, + description: data.description, + }); + return response; +} + +// 删除配置 +export async function deleteConfig(id: number): Promise { + return await request.delete(`/sys-config/${id}`); +} + +// 兼容性导出:保留 configApi 对象 +export const configApi = { + getList: getConfigsList, + getDetail: getConfigDetail, + getByKey: getConfigByKey, + create: createConfig, + update: updateConfig, + delete: deleteConfig, +}; + diff --git a/java-frontend/src/api/contests.ts b/java-frontend/src/api/contests.ts new file mode 100644 index 0000000..904efff --- /dev/null +++ b/java-frontend/src/api/contests.ts @@ -0,0 +1,1486 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +// ==================== 活动相关类型 ==================== +export interface Contest { + id: number; + contestName: string; + contestType: "individual" | "team"; + contestState: "unpublished" | "published"; + status: "ongoing" | "finished"; // 活动进度状态 + startTime: string; + endTime: string; + address?: string; + content?: string; + contestTenants?: number[]; + coverUrl?: string; + posterUrl?: string; + contactName?: string; + contactPhone?: string; + contactQrcode?: string; + organizers?: string; + coOrganizers?: string; + sponsors?: string; + // 报名配置 + registerStartTime: string; + registerEndTime: string; + registerState?: string; + requireAudit: boolean; // 是否需要审核 + allowedGrades?: number[]; // 允许报名的年级 + allowedClasses?: number[]; // 允许报名的班级 + teamMinMembers?: number; // 团队最少人数 + teamMaxMembers?: number; // 团队最多人数 + // 作品配置 + submitRule: "once" | "resubmit"; + submitStartTime: string; + submitEndTime: string; + workType?: "image" | "video" | "document" | "code" | "other"; // 作品类型 + workRequirement?: string; // 作品要求 + // 评审配置 + reviewRuleId?: number; + reviewStartTime: string; + reviewEndTime: string; + // 成果配置 + resultState: "unpublished" | "published"; // 成果发布状态 + resultPublishTime?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + attachments?: ContestAttachment[]; + reviewRule?: ContestReviewRule; + _count?: { + registrations: number; + works: number; + teams: number; + judges: number; + }; +} + +export interface CreateContestForm { + id?: number; // 编辑模式时使用 + contestName: string; + contestType: "individual" | "team"; + startTime: string; + endTime: string; + address?: string; + content?: string; + contestTenants?: number[]; + coverUrl?: string; + posterUrl?: string; + contactName?: string; + contactPhone?: string; + contactQrcode?: string; + organizers?: string; + coOrganizers?: string; + sponsors?: string; + // 报名配置 + registerStartTime: string; + registerEndTime: string; + requireAudit?: boolean; + allowedGrades?: number[]; + allowedClasses?: number[]; + teamMinMembers?: number; + teamMaxMembers?: number; + // 作品配置 + submitRule?: "once" | "resubmit"; + submitStartTime: string; + submitEndTime: string; + workType?: "image" | "video" | "document" | "code" | "other"; + workRequirement?: string; + // 评审配置 + reviewStartTime: string; + reviewEndTime: string; + resultPublishTime?: string; +} + +export interface UpdateContestForm extends Partial { + contestState?: "unpublished" | "published"; +} + +export interface QueryContestParams extends PaginationParams { + contestName?: string; + contestState?: "unpublished" | "published"; + status?: "ongoing" | "finished"; + contestType?: string; + visibility?: string; + stage?: "unpublished" | "registering" | "submitting" | "reviewing" | "finished"; + creatorTenantId?: number; + role?: "student" | "teacher" | "judge"; +} + +export interface ContestStats { + total: number; + unpublished: number; + registering: number; + submitting: number; + reviewing: number; + finished: number; +} + +// ==================== 附件相关类型 ==================== +export interface ContestAttachment { + id: number; + contestId: number; + fileName: string; + fileUrl: string; + format?: string; + fileType?: string; + size?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; +} + +export interface CreateAttachmentForm { + contestId: number; + fileName: string; + fileUrl: string; + format?: string; + fileType?: string; + size?: string; +} + +// ==================== 评审规则相关类型 ==================== +export interface ReviewDimension { + name: string; + percentage: number; + description?: string; +} + +export interface ContestReviewRule { + id: number; + tenantId: number; + ruleName: string; + ruleDescription?: string; + judgeCount: number; + dimensions: ReviewDimension[]; + calculationRule: "average" | "remove_max_min" | "remove_min"; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + contests?: Array<{ + id: number; + contestName: string; + }>; +} + +export interface CreateReviewRuleForm { + ruleName: string; + ruleDescription?: string; + judgeCount: number; + dimensions: ReviewDimension[]; + calculationRule?: "average" | "remove_max_min" | "remove_min"; +} + +// ==================== 报名相关类型 ==================== +export interface ContestRegistration { + id: number; + contestId: number; + tenantId: number; + registrationType?: "individual" | "team"; + teamId?: number; + teamName?: string; + userId: number; + accountNo: string; + accountName: string; + role?: string; + registrationState: "pending" | "passed" | "rejected" | "withdrawn"; + registrant?: number; + registrationTime: string; + reason?: string; + operator?: number; + operationDate?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + contest?: Contest; + user?: { + id: number; + username: string; + nickname: string; + gender?: string; + phone?: string; + student?: { + id: number; + gender?: number; + phone?: string; + class?: { + id: number; + name: string; + grade?: { + id: number; + name: string; + }; + }; + }; + }; + teachers?: Array<{ + id: number; + userId: number; + isDefault: boolean; + user: { + id: number; + username: string; + nickname: string; + }; + }>; + team?: ContestTeam; + _count?: { + works: number; + }; +} + +export interface CreateRegistrationForm { + contestId: number; + registrationType: "individual" | "team"; + teamId?: number; + userId: number; +} + +export interface ReviewRegistrationForm { + registrationState: "pending" | "passed" | "rejected" | "withdrawn"; + reason?: string; +} + +export interface QueryRegistrationParams extends PaginationParams { + contestId?: number; + registrationState?: "pending" | "passed" | "rejected" | "withdrawn"; + registrationType?: string; + userId?: number; + participantType?: "self" | "child"; + keyword?: string; +} + +export interface RegistrationStats { + total: number; + pending: number; + passed: number; + rejected: number; +} + +// ==================== 团队相关类型 ==================== +export interface ContestTeam { + id: number; + tenantId: number; + contestId: number; + teamName: string; + leaderUserId: number; + maxMembers?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + registrationState?: string; // 报名状态 + registrationId?: number; // 报名记录ID + leader?: { + id: number; + username: string; + nickname: string; + }; + members?: ContestTeamMember[]; + contest?: Contest; + _count?: { + members: number; + registrations: number; + }; +} + +export interface ContestTeamMember { + id: number; + tenantId: number; + teamId: number; + userId: number; + role: "leader" | "member" | "mentor"; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + user?: { + id: number; + username: string; + nickname: string; + }; +} + +export interface CreateTeamForm { + contestId: number; + teamName: string; + leaderId: number; + memberIds?: number[]; + teacherIds?: number[]; + maxMembers?: number; +} + +export interface InviteMemberForm { + userId: number; + role?: "leader" | "member" | "mentor"; +} + +// ==================== 作品相关类型 ==================== +export interface ContestWork { + id: number; + tenantId: number; + contestId: number; + registrationId: number; + workNo?: string; + title: string; + description?: string; + files?: string[]; + version: number; + isLatest: boolean; + status: "submitted" | "locked" | "reviewing" | "rejected" | "accepted"; + submitTime: string; + submitterUserId?: number; + submitterAccountNo?: string; + submitSource: string; + previewUrl?: string; + previewUrls?: string[]; + aiModelMeta?: any; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + contest?: Contest; + registration?: ContestRegistration; + attachments?: ContestWorkAttachment[]; + assignments?: Array<{ + id: number; + judgeId: number; + status: string; + judge?: { + id: number; + username: string; + nickname: string; + }; + }>; + _count?: { + scores: number; + assignments: number; + }; + // 评审统计字段(由后端计算返回) + reviewedCount?: number; + totalJudgesCount?: number; + averageScore?: number | null; +} + +export interface ContestWorkAttachment { + id: number; + tenantId: number; + contestId: number; + workId: number; + fileName: string; + fileUrl: string; + format?: string; + fileType?: string; + size?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; +} + +export interface SubmitWorkAttachment { + fileName: string; + fileUrl: string; + fileType?: string; + size?: string; +} + +export interface SubmitWorkForm { + registrationId: number; + title: string; + description?: string; + files?: string[]; + previewUrl?: string; + previewUrls?: string[]; + aiModelMeta?: any; + attachments?: SubmitWorkAttachment[]; +} + +export interface QueryWorkParams extends PaginationParams { + contestId?: number; + registrationId?: number; + status?: "submitted" | "locked" | "reviewing" | "rejected" | "accepted"; + title?: string; + workNo?: string; + username?: string; + keyword?: string; +} + +export interface WorksStats { + total: number; + submitted: number; + reviewing: number; + reviewed: number; +} + +// ==================== 评审相关类型 ==================== +export interface ContestWorkJudgeAssignment { + id: number; + contestId: number; + workId: number; + judgeId: number; + assignmentTime: string; + status: "assigned" | "reviewing" | "completed"; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + work?: ContestWork; + judge?: { + id: number; + username: string; + nickname: string; + }; + scores?: ContestWorkScore[]; +} + +export interface ContestWorkScore { + id: number; + tenantId: number; + contestId: number; + workId: number; + assignmentId: number; + judgeId: number; + judgeName: string; + dimensionScores: any; + totalScore: number; + comments?: string; + scoreTime: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + work?: ContestWork; + judge?: { + id: number; + username: string; + nickname: string; + }; +} + +export interface AssignWorkForm { + workId: number; + judgeIds: number[]; +} + +export interface CreateScoreForm { + workId: number; + assignmentId: number; + dimensionScores?: any; + totalScore: number; + comments?: string; +} + +// ==================== 公告相关类型 ==================== +export interface ContestNotice { + id: number; + contestId: number; + title: string; + content: string; + noticeType: "system" | "manual" | "urgent"; + priority: number; + publishTime?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + contest?: Contest; +} + +export interface CreateNoticeForm { + contestId: number; + title: string; + content: string; + noticeType?: "system" | "manual" | "urgent"; + priority?: number; +} + +// ==================== 评委相关类型 ==================== +export interface ContestJudge { + id: number; + contestId: number; + judgeId: number; + specialty?: string; + weight?: number; + description?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + contest?: Contest; + judge?: { + id: number; + username: string; + nickname: string; + email?: string; + phone?: string; + gender?: 'male' | 'female'; + status?: 'enabled' | 'disabled'; + tenantId?: number; + tenant?: { + id: number; + name: string; + }; + contestJudges?: Array<{ + contest: { + id: number; + contestName: string; + status: string; + }; + }>; + }; + _count?: { + assignedContestWorks: number; + scoredContestWorks: number; + }; +} + +export interface CreateJudgeForm { + contestId: number; + judgeId: number; + specialty?: string; + weight?: number; + description?: string; +} + +// ==================== API 函数 ==================== + +// 活动管理 +export const contestsApi = { + // 获取活动统计 + getStats: async (): Promise => { + const response = await request.get("/contests/stats"); + return response; + }, + + // 获取活动列表 + getList: async ( + params: QueryContestParams + ): Promise> => { + const response = await request.get>( + "/contests", + { params } + ); + return response; + }, + + // 获取我参与的活动列表 + getMyContests: async ( + params: QueryContestParams + ): Promise> => { + const response = await request.get>( + "/contests/my-contests", + { params } + ); + return response; + }, + + // 获取活动详情 + getDetail: async (id: number): Promise => { + const response = await request.get(`/contests/${id}`); + return response; + }, + + // 创建活动 + create: async (data: CreateContestForm): Promise => { + const response = await request.post("/contests", data); + return response; + }, + + // 更新活动 + update: async (id: number, data: UpdateContestForm): Promise => { + const response = await request.patch(`/contests/${id}`, data); + return response; + }, + + // 发布/撤回活动 + publish: async ( + id: number, + contestState: "unpublished" | "published" + ): Promise => { + const response = await request.patch( + `/contests/${id}/publish`, + { contestState } + ); + return response; + }, + + // 删除活动 + delete: async (id: number): Promise => { + return await request.delete(`/contests/${id}`); + }, + + // 标记活动完结 + finish: async (id: number): Promise => { + const response = await request.patch(`/contests/${id}/finish`); + return response; + }, + + // 重新开启已完结的活动 + reopen: async (id: number): Promise => { + const response = await request.patch(`/contests/${id}/reopen`); + return response; + }, +}; + +// 附件管理 +export const attachmentsApi = { + // 获取活动附件列表 + getList: async (contestId: number): Promise => { + const response = await request.get( + `/contests/attachments/contest/${contestId}` + ); + return response; + }, + + // 创建附件 + create: async (data: CreateAttachmentForm): Promise => { + const response = await request.post( + "/contests/attachments", + data + ); + return response; + }, + + // 删除附件 + delete: async (id: number): Promise => { + return await request.delete(`/contests/attachments/${id}`); + }, +}; + +// 评审规则 +export interface ReviewRule { + id: number; + tenantId: number; + ruleName: string; + ruleDescription?: string; + judgeCount: number; + dimensions: ReviewDimension[]; + calculationRule: "average" | "remove_max_min" | "remove_min"; + validState: number; + contests?: Array<{ + id: number; + contestName: string; + }>; + createTime?: string; + modifyTime?: string; +} + +// 用于选择器的简化评审规则 +export interface ReviewRuleForSelect { + id: number; + ruleName: string; + ruleDescription?: string; + judgeCount: number; + calculationRule: string; +} + +export interface QueryReviewRuleParams { + ruleName?: string; + page?: number; + pageSize?: number; +} + +export const reviewRulesApi = { + // 获取评审规则列表 + getList: async (params?: QueryReviewRuleParams): Promise<{ + list: ReviewRule[]; + total: number; + page: number; + pageSize: number; + }> => { + const response = await request.get("/contests/review-rules", { params }); + return response; + }, + + // 获取所有可用的评审规则(用于活动创建时选择) + getForSelect: async (): Promise => { + const response = await request.get( + "/contests/review-rules/select" + ); + return response; + }, + + // 获取评审规则详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/review-rules/${id}` + ); + return response; + }, + + // 创建评审规则 + create: async (data: CreateReviewRuleForm): Promise => { + const response = await request.post( + "/contests/review-rules", + data + ); + return response; + }, + + // 更新评审规则 + update: async ( + id: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/contests/review-rules/${id}`, + data + ); + return response; + }, + + // 删除评审规则 + delete: async (id: number): Promise => { + await request.delete(`/contests/review-rules/${id}`); + }, +}; + +// 报名管理 +export const registrationsApi = { + // 报名统计 + getStats: async (contestId?: number): Promise => { + const params: any = {}; + if (contestId) params.contestId = contestId; + const response = await request.get( + "/contests/registrations/stats", + { params } + ); + return response; + }, + + // 获取报名列表 + getList: async ( + params: QueryRegistrationParams + ): Promise> => { + const response = await request.get< + any, + PaginationResponse + >("/contests/registrations", { params }); + return response; + }, + + // 获取报名详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/registrations/${id}` + ); + return response; + }, + + // 获取当前用户在某活动中的报名记录(包括作为团队成员的情况) + getMyRegistration: async ( + contestId: number + ): Promise => { + const response = await request.get( + `/contests/registrations/my/${contestId}` + ); + return response; + }, + + // 创建报名 + create: async ( + data: CreateRegistrationForm + ): Promise => { + const response = await request.post( + "/contests/registrations", + data + ); + return response; + }, + + // 添加指导老师 + addTeacher: async ( + registrationId: number, + teacherUserId: number + ): Promise => { + const response = await request.post( + `/contests/registrations/${registrationId}/teachers`, + { teacherUserId } + ); + return response; + }, + + // 移除指导老师 + removeTeacher: async ( + registrationId: number, + teacherUserId: number + ): Promise => { + await request.delete( + `/contests/registrations/${registrationId}/teachers/${teacherUserId}` + ); + }, + + // 审核报名 + review: async ( + id: number, + data: ReviewRegistrationForm + ): Promise => { + const response = await request.patch( + `/contests/registrations/${id}/review`, + data + ); + return response; + }, + + // 删除报名 + delete: async (id: number): Promise => { + return await request.delete(`/contests/registrations/${id}`); + }, +}; + +// 团队管理 +export const teamsApi = { + // 获取团队列表 + getList: async (contestId: number): Promise => { + const response = await request.get( + `/contests/teams/contest/${contestId}` + ); + return response; + }, + + // 获取团队详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/teams/${id}` + ); + return response; + }, + + // 创建团队 + create: async (data: CreateTeamForm): Promise => { + const response = await request.post( + "/contests/teams", + data + ); + return response; + }, + + // 更新团队 + update: async ( + id: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/contests/teams/${id}`, + data + ); + return response; + }, + + // 邀请成员 + inviteMember: async ( + teamId: number, + data: InviteMemberForm + ): Promise => { + const response = await request.post( + `/contests/teams/${teamId}/members`, + data + ); + return response; + }, + + // 移除成员 + removeMember: async (teamId: number, userId: number): Promise => { + return await request.delete( + `/contests/teams/${teamId}/members/${userId}` + ); + }, + + // 删除团队 + delete: async (id: number): Promise => { + return await request.delete(`/contests/teams/${id}`); + }, +}; + +// 教师指导作品查询参数 +export interface QueryGuidedWorkParams extends PaginationParams { + contestId?: number; + workNo?: string; + playerName?: string; + accountNo?: string; +} + +// 教师指导的作品(含评审进度) +export interface GuidedWork extends ContestWork { + reviewProgress: string; +} + +// 作品管理 +export const worksApi = { + // 作品统计 + getStats: async (contestId?: number): Promise => { + const params: any = {}; + if (contestId) params.contestId = contestId; + const response = await request.get( + "/contests/works/stats", + { params } + ); + return response; + }, + + // 获取作品列表 + getList: async ( + params: QueryWorkParams + ): Promise> => { + const response = await request.get>( + "/contests/works", + { params } + ); + return response; + }, + + // 获取教师指导的作品列表 + getGuidedWorks: async ( + params: QueryGuidedWorkParams + ): Promise> => { + const response = await request.get>( + "/contests/works/guided", + { params } + ); + return response; + }, + + // 获取作品详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/works/${id}` + ); + return response; + }, + + // 提交作品 + submit: async (data: SubmitWorkForm): Promise => { + const response = await request.post( + "/contests/works/submit", + data + ); + return response; + }, + + // 获取作品版本列表 + getVersions: async (registrationId: number): Promise => { + const response = await request.get( + `/contests/works/registration/${registrationId}/versions` + ); + return response; + }, + + // 删除作品 + delete: async (id: number): Promise => { + return await request.delete(`/contests/works/${id}`); + }, +}; + +// ==================== 评审进度相关类型 ==================== +export interface ReviewProgress { + contest: { + id: number; + contestName: string; + reviewStartTime: string; + reviewEndTime: string; + reviewRule: ContestReviewRule | null; + }; + summary: { + totalWorks: number; + assignedWorksCount: number; + scoredWorksCount: number; + unassignedWorksCount: number; + totalJudges: number; + totalAssignments: number; + totalScores: number; + pendingScoresCount: number; + }; + progress: { + assignmentProgress: number; + scoringProgress: number; + overallProgress: number; + }; + judgeProgress: JudgeProgressItem[]; + unassignedWorks: UnassignedWork[]; + pendingAssignments: PendingAssignment[]; +} + +export interface JudgeProgressItem { + judgeId: number; + judgeName: string; + specialty: string | null; + weight: number | null; + assignedCount: number; + scoredCount: number; + pendingCount: number; + progress: number; +} + +export interface UnassignedWork { + id: number; + workNo: string | null; + title: string; + createTime: string; + registration: { + user: { id: number; nickname: string; username: string } | null; + team: { id: number; teamName: string } | null; + } | null; +} + +export interface PendingAssignment { + id: number; + workId: number; + workNo: string | null; + workTitle: string; + judgeId: number; + judgeName: string; + status: string; + assignmentTime: string; +} + +export interface WorkStatusStats { + submitted: number; + reviewing: number; + reviewed: number; + awarded: number; + total: number; +} + +export interface BatchAssignForm { + workIds: number[]; + judgeIds: number[]; +} + +export interface BatchAssignResult { + created: number; + skipped: number; + assignments: ContestWorkJudgeAssignment[]; +} + +export interface AutoAssignResult { + message: string; + worksCount?: number; + created: number; + judgesPerWork?: number; +} + +// 评审管理 +export const reviewsApi = { + // 分配作品给评委 + assignWork: async ( + contestId: number, + data: AssignWorkForm + ): Promise => { + const response = await request.post( + `/contests/reviews/assign?contestId=${contestId}`, + data + ); + return response; + }, + + // 批量分配作品给评委 + batchAssignWorks: async ( + contestId: number, + data: BatchAssignForm + ): Promise => { + const response = await request.post( + `/contests/reviews/batch-assign?contestId=${contestId}`, + data + ); + return response; + }, + + // 自动分配作品给评委 + autoAssignWorks: async (contestId: number): Promise => { + const response = await request.post( + `/contests/reviews/auto-assign?contestId=${contestId}` + ); + return response; + }, + + // 评分 + score: async (data: CreateScoreForm): Promise => { + const response = await request.post( + "/contests/reviews/score", + data + ); + return response; + }, + + // 更新评分 + updateScore: async ( + scoreId: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/contests/reviews/score/${scoreId}`, + data + ); + return response; + }, + + // 获取分配给当前评委的作品 + getAssignedWorks: async ( + contestId: number + ): Promise => { + const response = await request.get( + `/contests/reviews/assigned?contestId=${contestId}` + ); + return response; + }, + + // 获取评审进度统计 + getReviewProgress: async (contestId: number): Promise => { + const response = await request.get( + `/contests/reviews/progress/${contestId}` + ); + return response; + }, + + // 获取作品状态统计 + getWorkStatusStats: async (contestId: number): Promise => { + const response = await request.get( + `/contests/reviews/work-status/${contestId}` + ); + return response; + }, + + // 获取作品评分列表 + getWorkScores: async (workId: number): Promise => { + const response = await request.get( + `/contests/reviews/work/${workId}/scores` + ); + return response; + }, + + // 计算最终得分 + calculateFinalScore: async ( + workId: number + ): Promise<{ + finalScore: number; + scoreCount: number; + calculationRule: string; + }> => { + const response = await request.get< + any, + { finalScore: number; scoreCount: number; calculationRule: string } + >(`/contests/reviews/work/${workId}/final-score`); + return response; + }, + + // 替换评委 + replaceJudge: async ( + assignmentId: number, + newJudgeId: number + ): Promise => { + await request.post( + `/contests/reviews/replace-judge`, + { assignmentId, newJudgeId } + ); + }, + + // 获取评委参与的活动列表 + getJudgeContests: async (): Promise => { + const response = await request.get( + `/contests/reviews/judge/contests` + ); + return response; + }, + + // 获取评委在某个活动下分配的作品列表 + getJudgeContestWorks: async ( + contestId: number, + params: { + page?: number; + pageSize?: number; + workNo?: string; + accountNo?: string; + reviewStatus?: string; + } + ): Promise<{ list: any[]; total: number; page: number; pageSize: number }> => { + const response = await request.get< + any, + { list: any[]; total: number; page: number; pageSize: number } + >(`/contests/reviews/judge/contests/${contestId}/works`, { params }); + return response; + }, +}; + +// 公告管理 +export const noticesApi = { + // 获取公告列表(按活动) + getList: async (contestId: number): Promise => { + const response = await request.get( + `/contests/notices/contest/${contestId}` + ); + return response; + }, + + // 获取所有公告(支持搜索和分页) + getAll: async (params?: { + title?: string; + publishDate?: string; + page?: number; + pageSize?: number; + }): Promise> => { + const response = await request.get>( + '/contests/notices', + { params } + ); + return response; + }, + + // 获取公告详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/notices/${id}` + ); + return response; + }, + + // 创建公告 + create: async (data: CreateNoticeForm): Promise => { + const response = await request.post( + "/contests/notices", + data + ); + return response; + }, + + // 更新公告 + update: async ( + id: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/contests/notices/${id}`, + data + ); + return response; + }, + + // 删除公告 + delete: async (id: number): Promise => { + return await request.delete(`/contests/notices/${id}`); + }, +}; + +// ==================== 成果相关类型 ==================== +export interface ContestResult { + id: number; + workNo: string | null; + title: string; + finalScore: number | null; + rank: number | null; + awardLevel: string | null; + awardName: string | null; + certificateUrl: string | null; + registration: { + user: { id: number; username: string; nickname: string } | null; + team: { id: number; teamName: string } | null; + } | null; +} + +export interface ResultsResponse { + contest: { + id: number; + contestName: string; + resultState: string; + resultPublishTime: string | null; + reviewRule: ContestReviewRule | null; + }; + list: ContestResult[]; + total: number; + page: number; + pageSize: number; + awardDistribution: Record; +} + +export interface ResultsSummary { + contest: { + id: number; + contestName: string; + resultState: string; + resultPublishTime: string | null; + }; + summary: { + totalWorks: number; + scoredWorks: number; + rankedWorks: number; + awardedWorks: number; + unscoredWorks: number; + }; + awardDistribution: Record; + scoreStats: { + avgScore: string | null; + maxScore: string | null; + minScore: string | null; + }; +} + +export interface SetAwardForm { + awardLevel: 'first' | 'second' | 'third' | 'excellent' | 'none'; + awardName?: string; + certificateUrl?: string; +} + +export interface BatchSetAwardsForm { + awards: Array<{ + workId: number; + awardLevel: 'first' | 'second' | 'third' | 'excellent' | 'none'; + awardName?: string; + }>; +} + +export interface AutoSetAwardsForm { + first?: number; + second?: number; + third?: number; + excellent?: number; +} + +// 成果管理 +export const resultsApi = { + // 计算所有作品的最终得分 + calculateScores: async (contestId: number): Promise<{ message: string; calculatedCount: number; calculationRule: string }> => { + const response = await request.post( + `/contests/results/${contestId}/calculate-scores` + ); + return response; + }, + + // 计算排名 + calculateRankings: async (contestId: number): Promise<{ message: string; rankedCount: number }> => { + const response = await request.post( + `/contests/results/${contestId}/calculate-rankings` + ); + return response; + }, + + // 设置单个作品奖项 + setAward: async (workId: number, data: SetAwardForm): Promise => { + const response = await request.patch( + `/contests/results/work/${workId}/award`, + data + ); + return response; + }, + + // 批量设置奖项 + batchSetAwards: async (contestId: number, data: BatchSetAwardsForm): Promise => { + const response = await request.post( + `/contests/results/${contestId}/batch-set-awards`, + data + ); + return response; + }, + + // 根据排名自动设置奖项 + autoSetAwards: async (contestId: number, data: AutoSetAwardsForm): Promise => { + const response = await request.post( + `/contests/results/${contestId}/auto-set-awards`, + data + ); + return response; + }, + + // 发布成果 + publish: async (contestId: number): Promise => { + const response = await request.post( + `/contests/results/${contestId}/publish` + ); + return response; + }, + + // 撤回发布 + unpublish: async (contestId: number): Promise => { + const response = await request.post( + `/contests/results/${contestId}/unpublish` + ); + return response; + }, + + // 获取活动结果列表(作品列表) + getResults: async ( + contestId: number, + params: { + page?: number; + pageSize?: number; + workNo?: string; + accountNo?: string; + } = {} + ): Promise => { + const response = await request.get( + `/contests/results/${contestId}`, + { params: { page: params.page || 1, pageSize: params.pageSize || 10, ...params } } + ); + return response; + }, + + // 获取活动结果统计摘要 + getSummary: async (contestId: number): Promise => { + const response = await request.get( + `/contests/results/${contestId}/summary` + ); + return response; + }, +}; + +// 评委管理 +export const judgesApi = { + // 获取评委列表 + getList: async (contestId: number): Promise => { + const response = await request.get( + `/contests/judges/contest/${contestId}` + ); + return response; + }, + + // 获取评委详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/contests/judges/${id}` + ); + return response; + }, + + // 添加评委 + create: async (data: CreateJudgeForm): Promise => { + const response = await request.post( + "/contests/judges", + data + ); + return response; + }, + + // 更新评委 + update: async ( + id: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/contests/judges/${id}`, + data + ); + return response; + }, + + // 删除评委 + delete: async (id: number): Promise => { + return await request.delete(`/contests/judges/${id}`); + }, +}; diff --git a/java-frontend/src/api/departments.ts b/java-frontend/src/api/departments.ts new file mode 100644 index 0000000..c8ab698 --- /dev/null +++ b/java-frontend/src/api/departments.ts @@ -0,0 +1,91 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Department { + id: number; + tenantId: number; + name: string; + code: string; + parentId?: number | null; + description?: string; + sort: number; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + parent?: Department; + children?: Department[]; + _count?: { + teachers: number; + children: number; + }; +} + +export interface CreateDepartmentForm { + name: string; + code: string; + parentId?: number; + description?: string; + sort?: number; +} + +export interface UpdateDepartmentForm { + name?: string; + code?: string; + parentId?: number | null; + description?: string; + sort?: number; +} + +// 获取部门列表 +export async function getDepartmentsList( + params: PaginationParams & { parentId?: number } +): Promise> { + const response = await request.get>("/departments", { + params, + }); + return response; +} + +// 获取部门树 +export async function getDepartmentsTree(): Promise { + const response = await request.get("/departments/tree"); + return response; +} + +// 获取单个部门详情 +export async function getDepartmentDetail(id: number): Promise { + const response = await request.get(`/departments/${id}`); + return response; +} + +// 创建部门 +export async function createDepartment(data: CreateDepartmentForm): Promise { + const response = await request.post("/departments", data); + return response; +} + +// 更新部门 +export async function updateDepartment( + id: number, + data: UpdateDepartmentForm +): Promise { + const response = await request.patch(`/departments/${id}`, data); + return response; +} + +// 删除部门 +export async function deleteDepartment(id: number): Promise { + return await request.delete(`/departments/${id}`); +} + +export const departmentsApi = { + getList: getDepartmentsList, + getTree: getDepartmentsTree, + getDetail: getDepartmentDetail, + create: createDepartment, + update: updateDepartment, + delete: deleteDepartment, +}; + diff --git a/java-frontend/src/api/dict.ts b/java-frontend/src/api/dict.ts new file mode 100644 index 0000000..224c84f --- /dev/null +++ b/java-frontend/src/api/dict.ts @@ -0,0 +1,130 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Dict { + id: number; + name: string; + code: string; + description?: string; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + items?: DictItem[]; +} + +export interface DictItem { + id: number; + dictId: number; + label: string; + value: string; + sort: number; + validState: number; +} + +export interface CreateDictForm { + name: string; + code: string; + description?: string; +} + +export interface UpdateDictForm { + name?: string; + code?: string; + description?: string; +} + +// 获取字典列表 +export async function getDictsList( + params: PaginationParams +): Promise> { + const response = await request.get>("/dict/page", { + params, + }); + return response; +} + +// 获取单个字典详情 +export async function getDictDetail(id: number): Promise { + const response = await request.get(`/dict/${id}`); + return response; +} + +// 根据编码获取字典 +export async function getDictByCode(code: string): Promise { + const response = await request.get(`/dict/code/${code}`); + return response; +} + +// 创建字典 +export async function createDict(data: CreateDictForm): Promise { + const response = await request.post("/dict", data); + return response; +} + +// 更新字典 +export async function updateDict( + id: number, + data: UpdateDictForm +): Promise { + const response = await request.put(`/dict/${id}`, data); + return response; +} + +// 删除字典 +export async function deleteDict(id: number): Promise { + return await request.delete(`/dict/${id}`); +} + +// 兼容性导出:保留 dictApi 对象 +export const dictApi = { + getList: getDictsList, + getDetail: getDictDetail, + getByCode: getDictByCode, + create: createDict, + update: updateDict, + delete: deleteDict, +}; + +// ==================== 字典项管理 ==================== + +export interface CreateDictItemForm { + dictId: number; + label: string; + value: string; + sort?: number; +} + +export interface UpdateDictItemForm { + label?: string; + value?: string; + sort?: number; +} + +// 创建字典项 +export async function createDictItem(data: CreateDictItemForm): Promise { + const response = await request.post("/dict/item", data); + return response; +} + +// 获取字典项列表 +export async function getDictItems(dictId: number): Promise { + const response = await request.get(`/dict/item/${dictId}`); + return response; +} + +// 更新字典项 +export async function updateDictItem( + id: number, + data: UpdateDictItemForm +): Promise { + const response = await request.put(`/dict/item/${id}`, data); + return response; +} + +// 删除字典项 +export async function deleteDictItem(id: number): Promise { + return await request.delete(`/dict/item/${id}`); +} + diff --git a/java-frontend/src/api/grades.ts b/java-frontend/src/api/grades.ts new file mode 100644 index 0000000..a31079d --- /dev/null +++ b/java-frontend/src/api/grades.ts @@ -0,0 +1,78 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Grade { + id: number; + tenantId: number; + name: string; + code: string; + level: number; + description?: string; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + _count?: { + classes: number; + }; +} + +export interface CreateGradeForm { + name: string; + code: string; + level: number; + description?: string; +} + +export interface UpdateGradeForm { + name?: string; + code?: string; + level?: number; + description?: string; +} + +// 获取年级列表 +export async function getGradesList( + params: PaginationParams +): Promise> { + const response = await request.get>("/grades", { + params, + }); + return response; +} + +// 获取单个年级详情 +export async function getGradeDetail(id: number): Promise { + const response = await request.get(`/grades/${id}`); + return response; +} + +// 创建年级 +export async function createGrade(data: CreateGradeForm): Promise { + const response = await request.post("/grades", data); + return response; +} + +// 更新年级 +export async function updateGrade( + id: number, + data: UpdateGradeForm +): Promise { + const response = await request.patch(`/grades/${id}`, data); + return response; +} + +// 删除年级 +export async function deleteGrade(id: number): Promise { + return await request.delete(`/grades/${id}`); +} + +export const gradesApi = { + getList: getGradesList, + getDetail: getGradeDetail, + create: createGrade, + update: updateGrade, + delete: deleteGrade, +}; + diff --git a/java-frontend/src/api/homework.ts b/java-frontend/src/api/homework.ts new file mode 100644 index 0000000..cdb5a30 --- /dev/null +++ b/java-frontend/src/api/homework.ts @@ -0,0 +1,415 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +// ==================== 作业相关类型 ==================== +export interface Homework { + id: number; + tenantId: number; + name: string; + content?: string; + status: "unpublished" | "published"; + publishTime?: string; + submitStartTime: string; + submitEndTime: string; + attachments?: HomeworkAttachment[]; + publishScope?: number[]; + publishScopeNames?: string[]; + reviewRuleId?: number; + reviewRule?: HomeworkReviewRule; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + _count?: { + submissions: number; + }; + // 学生端:我的提交记录 + submission?: { + id: number; + workName: string; + submitTime: string; + totalScore?: number; + } | null; +} + +export interface HomeworkAttachment { + fileName: string; + fileUrl: string; + size?: string; +} + +export interface CreateHomeworkForm { + name: string; + content?: string; + submitStartTime: string; + submitEndTime: string; + attachments?: HomeworkAttachment[]; + publishScope?: number[]; + reviewRuleId?: number; +} + +export interface UpdateHomeworkForm extends Partial {} + +export interface QueryHomeworkParams extends PaginationParams { + name?: string; + status?: string; + submitStartTime?: string; + submitEndTime?: string; +} + +// ==================== 提交记录相关类型 ==================== +export interface HomeworkSubmission { + id: number; + tenantId: number; + homeworkId: number; + studentId: number; + workNo?: string; + workName: string; + workDescription?: string; + files?: any[]; + attachments?: any[]; + submitTime: string; + status: "pending" | "reviewed" | "rejected"; + totalScore?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + homework?: { + id: number; + name: string; + reviewRuleId?: number; + reviewRule?: HomeworkReviewRule; + }; + student?: { + id: number; + username: string; + nickname: string; + student?: { + studentNo?: string; + class?: { + id: number; + name: string; + grade?: { + id: number; + name: string; + }; + }; + }; + }; + scores?: HomeworkScore[]; + _count?: { + scores: number; + }; +} + +export interface QuerySubmissionParams extends PaginationParams { + homeworkId?: number; + workNo?: string; + workName?: string; + studentAccount?: string; + studentName?: string; + status?: string; + classIds?: number[]; + gradeId?: number; +} + +// ==================== 评审规则相关类型 ==================== +export interface HomeworkReviewRule { + id: number; + tenantId: number; + name: string; + description?: string; + criteria: ReviewCriterion[]; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + homeworks?: Array<{ + id: number; + name: string; + }>; +} + +export interface ReviewCriterion { + name: string; + maxScore: number; + description?: string; +} + +export interface CreateReviewRuleForm { + name: string; + description?: string; + criteria: ReviewCriterion[]; +} + +// ==================== 评分相关类型 ==================== +export interface HomeworkScore { + id: number; + tenantId: number; + submissionId: number; + reviewerId: number; + dimensionScores: DimensionScore[]; + totalScore: number; + comments?: string; + scoreTime: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + validState?: number; + reviewer?: { + id: number; + nickname: string; + }; +} + +export interface DimensionScore { + name: string; + score: number; + maxScore: number; +} + +export interface CreateScoreForm { + submissionId: number; + dimensionScores: DimensionScore[]; + comments?: string; +} + +// ==================== 班级树相关类型 ==================== +export interface ClassTreeNode { + id: string | number; + name: string; + type: "grade" | "class"; + gradeId?: number; + classId?: number; + children?: ClassTreeNode[]; +} + +// ==================== API 函数 ==================== + +// 作业管理 +export const homeworksApi = { + // 获取作业列表(教师端) + getList: async ( + params: QueryHomeworkParams + ): Promise> => { + const response = await request.get>( + "/homework/homeworks", + { params } + ); + return response; + }, + + // 获取我的作业列表(学生端) + getMyList: async ( + params: QueryHomeworkParams + ): Promise> => { + const response = await request.get>( + "/homework/homeworks/my", + { params } + ); + return response; + }, + + // 获取作业详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/homework/homeworks/${id}` + ); + return response; + }, + + // 创建作业 + create: async (data: CreateHomeworkForm): Promise => { + const response = await request.post( + "/homework/homeworks", + data + ); + return response; + }, + + // 更新作业 + update: async (id: number, data: UpdateHomeworkForm): Promise => { + const response = await request.patch( + `/homework/homeworks/${id}`, + data + ); + return response; + }, + + // 发布作业 + publish: async (id: number, publishScope: number[]): Promise => { + const response = await request.post( + `/homework/homeworks/${id}/publish`, + { publishScope } + ); + return response; + }, + + // 取消发布作业 + unpublish: async (id: number): Promise => { + const response = await request.post( + `/homework/homeworks/${id}/unpublish` + ); + return response; + }, + + // 删除作业 + delete: async (id: number): Promise => { + return await request.delete(`/homework/homeworks/${id}`); + }, +}; + +// 学生提交作业表单 +export interface SubmitHomeworkForm { + homeworkId: number; + workName: string; + workDescription?: string; + files?: HomeworkAttachment[]; +} + +// 提交记录管理 +export const submissionsApi = { + // 获取提交记录列表 + getList: async ( + params: QuerySubmissionParams + ): Promise> => { + const response = await request.get< + any, + PaginationResponse + >("/homework/submissions", { params }); + return response; + }, + + // 获取提交记录详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/homework/submissions/${id}` + ); + return response; + }, + + // 获取班级树结构 + getClassTree: async (): Promise => { + const response = await request.get( + "/homework/submissions/class-tree" + ); + return response; + }, + + // 获取当前用户对某作业的提交记录 + getMySubmission: async (homeworkId: number): Promise => { + const response = await request.get( + `/homework/submissions/my/${homeworkId}` + ); + return response; + }, + + // 提交作业 + submit: async (data: SubmitHomeworkForm): Promise => { + const response = await request.post( + "/homework/submissions", + data + ); + return response; + }, +}; + +// 评审规则管理 +export const reviewRulesApi = { + // 获取评审规则列表 + getList: async (params?: { + name?: string; + page?: number; + pageSize?: number; + }): Promise<{ + list: HomeworkReviewRule[]; + total: number; + page: number; + pageSize: number; + }> => { + const response = await request.get< + any, + { + list: HomeworkReviewRule[]; + total: number; + page: number; + pageSize: number; + } + >("/homework/review-rules", { params }); + return response; + }, + + // 获取所有可用的评审规则(用于选择) + getForSelect: async (): Promise => { + const response = await request.get( + "/homework/review-rules/select" + ); + return response; + }, + + // 获取评审规则详情 + getDetail: async (id: number): Promise => { + const response = await request.get( + `/homework/review-rules/${id}` + ); + return response; + }, + + // 创建评审规则 + create: async (data: CreateReviewRuleForm): Promise => { + const response = await request.post( + "/homework/review-rules", + data + ); + return response; + }, + + // 更新评审规则 + update: async ( + id: number, + data: Partial + ): Promise => { + const response = await request.patch( + `/homework/review-rules/${id}`, + data + ); + return response; + }, + + // 删除评审规则 + delete: async (id: number): Promise => { + await request.delete(`/homework/review-rules/${id}`); + }, +}; + +// 评分管理 +export const scoresApi = { + // 提交评分 + create: async (data: CreateScoreForm): Promise => { + const response = await request.post( + "/homework/scores", + data + ); + return response; + }, + + // 标记作品违规 + markViolation: async ( + submissionId: number, + reason?: string + ): Promise => { + await request.post(`/homework/scores/${submissionId}/violation`, { + reason, + }); + }, + + // 重置评分 + resetScore: async (submissionId: number): Promise => { + await request.post(`/homework/scores/${submissionId}/reset`); + }, +}; diff --git a/java-frontend/src/api/judges-management.ts b/java-frontend/src/api/judges-management.ts new file mode 100644 index 0000000..13a3996 --- /dev/null +++ b/java-frontend/src/api/judges-management.ts @@ -0,0 +1,156 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Judge { + id: number; + username: string; + nickname: string; + email?: string; + phone?: string; + gender?: 'male' | 'female'; + avatar?: string; + status?: 'enabled' | 'disabled'; + organization?: string; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + roles?: Array<{ + id: number; + role: { + id: number; + name: string; + code: string; + }; + }>; + contestJudges?: Array<{ + contest: { + id: number; + contestName: string; + status: string; + contestState?: string; + }; + }>; +} + +export interface QueryJudgeParams extends PaginationParams { + organization?: string; + nickname?: string; + username?: string; + status?: 'enabled' | 'disabled'; +} + +export interface CreateJudgeForm { + nickname: string; + gender: 'male' | 'female'; + organization: string; + phone: string; + password: string; + username?: string; + email?: string; + avatar?: string; + status?: 'enabled' | 'disabled'; +} + +export interface UpdateJudgeForm { + nickname?: string; + gender?: 'male' | 'female'; + organization?: string; + phone?: string; + password?: string; + username?: string; + email?: string; + avatar?: string; + status?: 'enabled' | 'disabled'; +} + +export interface JudgeListResponse { + list: Judge[]; + total: number; + page: number; + pageSize: number; +} + +// 获取评委列表 +export async function getJudgesList( + params: QueryJudgeParams +): Promise { + const response = await request.get( + "/judges-management", + { + params, + } + ); + return response; +} + +// 获取单个评委详情 +export async function getJudgeDetail(id: number): Promise { + const response = await request.get(`/judges-management/${id}`); + return response; +} + +// 创建评委 +export async function createJudge(data: CreateJudgeForm): Promise { + const response = await request.post("/judges-management", data); + return response; +} + +// 更新评委 +export async function updateJudge( + id: number, + data: UpdateJudgeForm +): Promise { + const response = await request.patch( + `/judges-management/${id}`, + data + ); + return response; +} + +// 删除评委 +export async function deleteJudge(id: number): Promise { + return await request.delete(`/judges-management/${id}`); +} + +// 冻结评委 +export async function freezeJudge(id: number): Promise { + const response = await request.patch( + `/judges-management/${id}/freeze` + ); + return response; +} + +// 解冻评委 +export async function unfreezeJudge(id: number): Promise { + const response = await request.patch( + `/judges-management/${id}/unfreeze` + ); + return response; +} + +// 批量删除评委 +export async function batchDeleteJudges(ids: number[]): Promise { + return await request.post("/judges-management/batch-delete", { + ids, + }); +} + +// 兼容性导出:保留 judgesManagementApi 对象 +export const judgesManagementApi = { + getList: getJudgesList, + getDetail: getJudgeDetail, + create: createJudge, + update: updateJudge, + delete: deleteJudge, + freeze: freezeJudge, + unfreeze: unfreezeJudge, + batchDelete: batchDeleteJudges, +}; + + + + + + diff --git a/java-frontend/src/api/logs.ts b/java-frontend/src/api/logs.ts new file mode 100644 index 0000000..ba252a2 --- /dev/null +++ b/java-frontend/src/api/logs.ts @@ -0,0 +1,90 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Log { + id: number; + userId?: number; + action: string; + content?: string; + ip?: string; + userAgent?: string; + createTime?: string; + user?: { + id: number; + username: string; + nickname: string; + }; +} + +export interface LogQueryParams extends PaginationParams { + userId?: number; + action?: string; + keyword?: string; + ip?: string; + startTime?: string; + endTime?: string; +} + +export interface LogStatistics { + totalCount: number; + recentCount: number; + days: number; + actionStats: Array<{ + action: string; + count: number; + }>; + dailyStats: Array<{ + date: string; + count: number; + }>; +} + +// 获取日志列表 +export async function getLogsList( + params: LogQueryParams +): Promise> { + const response = await request.get>("/sys-log/page", { + params, + }); + return response; +} + +// 获取单个日志详情 +export async function getLogDetail(id: number): Promise { + const response = await request.get(`/sys-log/${id}`); + return response; +} + +// 获取日志统计信息 +export async function getLogStatistics(days: number = 7): Promise { + const response = await request.get("/sys-log/statistics", { + params: { days }, + }); + return response; +} + +// 批量删除日志 +export async function deleteLogs(ids: number[]): Promise { + const response = await request.delete("/sys-log", { + params: { ids }, + }); + return response; +} + +// 清理过期日志 +export async function cleanOldLogs(daysToKeep?: number): Promise { + const response = await request.post("/sys-log/clean", null, { + params: daysToKeep ? { days: daysToKeep } : {}, + }); + return response; +} + +// 兼容性导出:保留 logsApi 对象 +export const logsApi = { + getList: getLogsList, + getDetail: getLogDetail, + getStatistics: getLogStatistics, + delete: deleteLogs, + clean: cleanOldLogs, +}; + diff --git a/java-frontend/src/api/menus.ts b/java-frontend/src/api/menus.ts new file mode 100644 index 0000000..79f39e4 --- /dev/null +++ b/java-frontend/src/api/menus.ts @@ -0,0 +1,87 @@ +import request from "@/utils/request"; + +export interface Menu { + id: number; + name: string; + path?: string; + icon?: string; + component?: string; + parentId?: number; + permission?: string; + sort: number; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + children?: Menu[]; + parent?: Menu; +} + +export interface CreateMenuForm { + name: string; + path?: string; + icon?: string; + component?: string; + parentId?: number; + permission?: string; + sort?: number; +} + +export interface UpdateMenuForm { + name?: string; + path?: string; + icon?: string; + component?: string; + parentId?: number; + permission?: string; + sort?: number; +} + +// 获取菜单列表(树形结构) +export async function getMenusList(): Promise { + const response = await request.get("/menus"); + return response; +} + +// 获取单个菜单详情 +export async function getMenuDetail(id: number): Promise { + const response = await request.get(`/menus/${id}`); + return response; +} + +// 创建菜单 +export async function createMenu(data: CreateMenuForm): Promise { + const response = await request.post("/menus", data); + return response; +} + +// 更新菜单 +export async function updateMenu( + id: number, + data: UpdateMenuForm +): Promise { + const response = await request.patch(`/menus/${id}`, data); + return response; +} + +// 删除菜单 +export async function deleteMenu(id: number): Promise { + return await request.delete(`/menus/${id}`); +} + +// 获取当前用户的菜单(根据权限过滤) +export async function getUserMenus(): Promise { + const response = await request.get("/menus/user-menus"); + return response; +} + +// 兼容性导出:保留 menusApi 对象 +export const menusApi = { + getList: getMenusList, + getDetail: getMenuDetail, + create: createMenu, + update: updateMenu, + delete: deleteMenu, + getUserMenus: getUserMenus, +}; diff --git a/java-frontend/src/api/permissions.ts b/java-frontend/src/api/permissions.ts new file mode 100644 index 0000000..28c5c81 --- /dev/null +++ b/java-frontend/src/api/permissions.ts @@ -0,0 +1,78 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Permission { + id: number; + name: string; + code: string; + resource: string; + action: string; + description?: string; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; +} + +export interface CreatePermissionForm { + name: string; + code: string; + resource: string; + action: string; + description?: string; +} + +export interface UpdatePermissionForm { + name?: string; + code?: string; + resource?: string; + action?: string; + description?: string; +} + +// 获取权限列表 +export async function getPermissionsList( + params: PaginationParams +): Promise> { + const response = await request.get>("/permissions", { + params, + }); + return response; +} + +// 获取单个权限详情 +export async function getPermissionDetail(id: number): Promise { + const response = await request.get(`/permissions/${id}`); + return response; +} + +// 创建权限 +export async function createPermission(data: CreatePermissionForm): Promise { + const response = await request.post("/permissions", data); + return response; +} + +// 更新权限 +export async function updatePermission( + id: number, + data: UpdatePermissionForm +): Promise { + const response = await request.patch(`/permissions/${id}`, data); + return response; +} + +// 删除权限 +export async function deletePermission(id: number): Promise { + return await request.delete(`/permissions/${id}`); +} + +// 兼容性导出:保留 permissionsApi 对象 +export const permissionsApi = { + getList: getPermissionsList, + getDetail: getPermissionDetail, + create: createPermission, + update: updatePermission, + delete: deletePermission, +}; + diff --git a/java-frontend/src/api/preset-comments.ts b/java-frontend/src/api/preset-comments.ts new file mode 100644 index 0000000..318b0a4 --- /dev/null +++ b/java-frontend/src/api/preset-comments.ts @@ -0,0 +1,139 @@ +import request from "@/utils/request"; + +export interface PresetComment { + id: number; + contestId: number; + judgeId: number; + content: string; + score?: number; + sortOrder: number; + useCount: number; + validState: number; + creator?: number; + modifier?: number; + createTime: string; + modifyTime: string; +} + +export interface CreatePresetCommentParams { + contestId: number; + content: string; + score?: number; + sortOrder?: number; +} + +export interface UpdatePresetCommentParams { + content?: string; + score?: number; + sortOrder?: number; +} + +export interface SyncPresetCommentsParams { + sourceContestId: number; + targetContestIds: number[]; +} + +export interface JudgeContest { + id: number; + contestName: string; + contestState: string; + status: string; +} + +// 获取预设评语列表 +export async function getPresetCommentsList( + contestId: number +): Promise { + const response = await request.get( + "/contests/preset-comments", + { + params: { contestId }, + } + ); + return response; +} + +// 获取单个预设评语详情 +export async function getPresetCommentDetail( + id: number +): Promise { + const response = await request.get( + `/contests/preset-comments/${id}` + ); + return response; +} + +// 创建预设评语 +export async function createPresetComment( + data: CreatePresetCommentParams +): Promise { + const response = await request.post( + "/contests/preset-comments", + data + ); + return response; +} + +// 更新预设评语 +export async function updatePresetComment( + id: number, + data: UpdatePresetCommentParams +): Promise { + const response = await request.patch( + `/contests/preset-comments/${id}`, + data + ); + return response; +} + +// 删除预设评语 +export async function deletePresetComment(id: number): Promise { + return await request.delete(`/contests/preset-comments/${id}`); +} + +// 批量删除预设评语 +export async function batchDeletePresetComments(ids: number[]): Promise { + return await request.post("/contests/preset-comments/batch-delete", { + ids, + }); +} + +// 同步预设评语到其他活动 +export async function syncPresetComments( + data: SyncPresetCommentsParams +): Promise<{ message: string; count: number }> { + const response = await request.post( + "/contests/preset-comments/sync", + data + ); + return response; +} + +// 获取评委的活动列表 +export async function getJudgeContests(): Promise { + const response = await request.get( + "/contests/preset-comments/judge/contests" + ); + return response; +} + +// 增加使用次数 +export async function incrementUseCount(id: number): Promise { + const response = await request.post( + `/contests/preset-comments/${id}/use` + ); + return response; +} + +// 兼容性导出:保留 presetCommentsApi 对象 +export const presetCommentsApi = { + getList: getPresetCommentsList, + getDetail: getPresetCommentDetail, + create: createPresetComment, + update: updatePresetComment, + delete: deletePresetComment, + batchDelete: batchDeletePresetComments, + sync: syncPresetComments, + getJudgeContests: getJudgeContests, + incrementUseCount: incrementUseCount, +}; diff --git a/java-frontend/src/api/public.ts b/java-frontend/src/api/public.ts new file mode 100644 index 0000000..eddb33d --- /dev/null +++ b/java-frontend/src/api/public.ts @@ -0,0 +1,404 @@ +import axios from "axios" + +// 公众端专用 axios 实例 +const publicApi = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || "/api", + timeout: 15000, +}) + +// 请求拦截器 +publicApi.interceptors.request.use((config) => { + const token = localStorage.getItem("public_token") + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +// 响应拦截器 +publicApi.interceptors.response.use( + (response) => response.data?.data ?? response.data, + (error) => { + if (error.response?.status === 401) { + localStorage.removeItem("public_token") + localStorage.removeItem("public_user") + // 如果在公众端页面,跳转到公众端登录 + if (window.location.pathname.startsWith("/p/")) { + window.location.href = "/p/login" + } + } + return Promise.reject(error) + }, +) + +// ==================== 认证 ==================== + +export interface PublicRegisterParams { + username: string + password: string + nickname: string + phone?: string + city?: string +} + +export interface PublicLoginParams { + username: string + password: string +} + +export interface PublicUser { + id: number + username: string + nickname: string + phone: string | null + city: string | null + avatar: string | null + tenantId: number + tenantCode: string + userSource: string + userType: "adult" | "child" + parentUserId: number | null + roles: string[] + permissions: string[] + children?: any[] + childrenCount?: number +} + +export interface LoginResponse { + token: string + user: PublicUser +} + +export const publicAuthApi = { + register: (data: PublicRegisterParams): Promise => + publicApi.post("/public/auth/register", data), + + login: (data: PublicLoginParams): Promise => + publicApi.post("/public/auth/login", data), +} + +// ==================== 个人信息 ==================== + +export const publicProfileApi = { + getProfile: (): Promise => publicApi.get("/public/mine/profile"), + + updateProfile: (data: { + nickname?: string + city?: string + avatar?: string + gender?: string + }) => publicApi.put("/public/mine/profile", data), +} + +// ==================== 子女管理 ==================== + +export interface Child { + id: number + parentId: number + name: string + gender: string | null + birthday: string | null + grade: string | null + city: string | null + schoolName: string | null + avatar: string | null +} + +export interface CreateChildParams { + name: string + gender?: string + birthday?: string + grade?: string + city?: string + schoolName?: string +} + +export const publicChildrenApi = { + list: (): Promise => publicApi.get("/public/mine/children"), + + create: (data: CreateChildParams): Promise => + publicApi.post("/public/mine/children", data), + + get: (id: number): Promise => + publicApi.get(`/public/mine/children/${id}`), + + update: (id: number, data: Partial): Promise => + publicApi.put(`/public/mine/children/${id}`, data), + + delete: (id: number) => publicApi.delete(`/public/mine/children/${id}`), +} + +// ==================== 子女独立账号管理 ==================== + +export interface CreateChildAccountParams { + username: string + password: string + nickname: string + gender?: string + birthday?: string + city?: string + avatar?: string + relationship?: string +} + +export interface ChildAccount { + id: number + username: string + nickname: string + avatar: string | null + gender: string | null + birthday: string | null + city: string | null + status: string + userType: string + createTime: string + relationship: string | null + controlMode: string +} + +export const publicChildAccountApi = { + // 家长为子女创建独立账号 + create: (data: CreateChildAccountParams): Promise => + publicApi.post("/public/children/create-account", data), + + // 获取子女账号列表 + list: (): Promise => + publicApi.get("/public/children/accounts"), + + // 家长切换到子女身份 + switchToChild: (childUserId: number): Promise => + publicApi.post("/public/auth/switch-child", { childUserId }), + + // 更新子女账号信息 + update: (id: number, data: { + nickname?: string + password?: string + gender?: string + birthday?: string + city?: string + avatar?: string + controlMode?: string + }): Promise => + publicApi.put(`/public/children/accounts/${id}`, data), + + // 子女查看家长信息 + getParentInfo: (): Promise<{ + parentId: number + nickname: string + avatar: string | null + relationship: string | null + } | null> => + publicApi.get("/public/mine/parent-info"), +} + +// ==================== 活动 ==================== + +export interface PublicActivity { + id: number + contestName: string + contestType: string + contestState: string + status: string + startTime: string + endTime: string + coverUrl: string | null + posterUrl: string | null + registerStartTime: string + registerEndTime: string + submitStartTime: string + submitEndTime: string + organizers: any + visibility: string +} + +export const publicActivitiesApi = { + list: (params?: { + page?: number + pageSize?: number + keyword?: string + contestType?: string + }): Promise<{ list: PublicActivity[]; total: number }> => + publicApi.get("/public/activities", { params }), + + detail: (id: number) => publicApi.get(`/public/activities/${id}`), + + register: ( + id: number, + data: { participantType: "self" | "child"; childId?: number }, + ) => publicApi.post(`/public/activities/${id}/register`, data), + + getMyRegistration: (id: number) => + publicApi.get(`/public/activities/${id}/my-registration`), + + submitWork: ( + id: number, + data: { + registrationId: number + title: string + description?: string + files?: string[] + previewUrl?: string + attachments?: { fileName: string; fileUrl: string; fileType?: string; size?: string }[] + }, + ) => publicApi.post(`/public/activities/${id}/submit-work`, data), +} + +// ==================== 我的报名 & 作品 ==================== + +export const publicMineApi = { + registrations: (params?: { page?: number; pageSize?: number }) => + publicApi.get("/public/mine/registrations", { params }), + + works: (params?: { page?: number; pageSize?: number }) => + publicApi.get("/public/mine/works", { params }), +} + +// ==================== 用户作品库 ==================== + +export interface UserWork { + id: number + userId: number + title: string + coverUrl: string | null + description: string | null + visibility: string + status: string + reviewNote: string | null + originalImageUrl: string | null + voiceInputUrl: string | null + textInput: string | null + aiMeta: any + viewCount: number + likeCount: number + favoriteCount: number + commentCount: number + shareCount: number + publishTime: string | null + createTime: string + modifyTime: string + pages?: UserWorkPage[] + tags?: Array<{ tag: { id: number; name: string; category: string } }> + creator?: { id: number; nickname: string; avatar: string | null; username: string } + _count?: { pages: number; likes: number; favorites: number; comments: number } +} + +export interface UserWorkPage { + id: number + workId: number + pageNo: number + imageUrl: string | null + text: string | null + audioUrl: string | null +} + +export const publicUserWorksApi = { + // 创建作品 + create: (data: { + title: string + coverUrl?: string + description?: string + visibility?: string + originalImageUrl?: string + voiceInputUrl?: string + textInput?: string + aiMeta?: any + pages?: Array<{ pageNo: number; imageUrl?: string; text?: string; audioUrl?: string }> + tagIds?: number[] + }): Promise => publicApi.post("/public/works", data), + + // 我的作品列表 + list: (params?: { + page?: number + pageSize?: number + status?: string + keyword?: string + }): Promise<{ list: UserWork[]; total: number }> => + publicApi.get("/public/works", { params }), + + // 作品详情 + detail: (id: number): Promise => + publicApi.get(`/public/works/${id}`), + + // 更新作品 + update: (id: number, data: { + title?: string + description?: string + coverUrl?: string + visibility?: string + tagIds?: number[] + }): Promise => publicApi.put(`/public/works/${id}`, data), + + // 删除作品 + delete: (id: number) => publicApi.delete(`/public/works/${id}`), + + // 发布作品(进入审核) + publish: (id: number) => publicApi.post(`/public/works/${id}/publish`), + + // 获取绘本分页 + getPages: (id: number): Promise => + publicApi.get(`/public/works/${id}/pages`), + + // 保存绘本分页 + savePages: (id: number, pages: Array<{ pageNo: number; imageUrl?: string; text?: string; audioUrl?: string }>) => + publicApi.post(`/public/works/${id}/pages`, { pages }), +} + +// ==================== AI 创作流程 ==================== + +export const publicCreationApi = { + // 提交创作请求 + submit: (data: { + originalImageUrl: string + voiceInputUrl?: string + textInput?: string + }): Promise<{ id: number; status: string; message: string }> => + publicApi.post("/public/creation/submit", data), + + // 查询生成进度 + getStatus: (id: number): Promise<{ id: number; status: string; title: string; createdAt: string }> => + publicApi.get(`/public/creation/${id}/status`), + + // 获取生成结果 + getResult: (id: number): Promise => + publicApi.get(`/public/creation/${id}/result`), + + // 创作历史 + history: (params?: { page?: number; pageSize?: number }): Promise<{ list: any[]; total: number }> => + publicApi.get("/public/creation/history", { params }), +} + +// ==================== 标签 ==================== + +export interface WorkTag { + id: number + name: string + category: string | null + usageCount: number +} + +export const publicTagsApi = { + list: (): Promise => publicApi.get("/public/tags"), + hot: (): Promise => publicApi.get("/public/tags/hot"), +} + +// ==================== 作品广场 ==================== + +export const publicGalleryApi = { + list: (params?: { + page?: number + pageSize?: number + tagId?: number + category?: string + sortBy?: string + keyword?: string + }): Promise<{ list: UserWork[]; total: number }> => + publicApi.get("/public/gallery", { params }), + + detail: (id: number): Promise => + publicApi.get(`/public/gallery/${id}`), + + userWorks: (userId: number, params?: { page?: number; pageSize?: number }): Promise<{ list: UserWork[]; total: number }> => + publicApi.get(`/public/users/${userId}/works`, { params }), +} + +export default publicApi diff --git a/java-frontend/src/api/roles.ts b/java-frontend/src/api/roles.ts new file mode 100644 index 0000000..56e8aae --- /dev/null +++ b/java-frontend/src/api/roles.ts @@ -0,0 +1,83 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Role { + id: number; + name: string; + code: string; + description?: string; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + permissions?: Array<{ + id: number; + permission: { + id: number; + name: string; + code: string; + resource: string; + action: string; + }; + }>; +} + +export interface CreateRoleForm { + name: string; + code: string; + description?: string; + permissionIds?: number[]; +} + +export interface UpdateRoleForm { + name?: string; + code?: string; + description?: string; + permissionIds?: number[]; +} + +// 获取角色列表 +export async function getRolesList( + params: PaginationParams +): Promise> { + const response = await request.get>("/roles", { + params, + }); + return response; +} + +// 获取单个角色详情 +export async function getRoleDetail(id: number): Promise { + const response = await request.get(`/roles/${id}`); + return response; +} + +// 创建角色 +export async function createRole(data: CreateRoleForm): Promise { + const response = await request.post("/roles", data); + return response; +} + +// 更新角色 +export async function updateRole( + id: number, + data: UpdateRoleForm +): Promise { + const response = await request.patch(`/roles/${id}`, data); + return response; +} + +// 删除角色 +export async function deleteRole(id: number): Promise { + return await request.delete(`/roles/${id}`); +} + +// 兼容性导出:保留 rolesApi 对象 +export const rolesApi = { + getList: getRolesList, + getDetail: getRoleDetail, + create: createRole, + update: updateRole, + delete: deleteRole, +}; diff --git a/java-frontend/src/api/schools.ts b/java-frontend/src/api/schools.ts new file mode 100644 index 0000000..4578372 --- /dev/null +++ b/java-frontend/src/api/schools.ts @@ -0,0 +1,73 @@ +import request from "@/utils/request"; + +export interface School { + id: number; + tenantId: number; + address?: string; + phone?: string; + principal?: string; + established?: string; + description?: string; + logo?: string; + website?: string; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + tenant?: { + id: number; + name: string; + code: string; + }; +} + +export interface CreateSchoolForm { + address?: string; + phone?: string; + principal?: string; + established?: string; + description?: string; + logo?: string; + website?: string; +} + +export interface UpdateSchoolForm { + address?: string; + phone?: string; + principal?: string; + established?: string; + description?: string; + logo?: string; + website?: string; +} + +// 获取学校信息 +export async function getSchool(): Promise { + const response = await request.get("/schools"); + return response; +} + +// 创建学校信息 +export async function createSchool(data: CreateSchoolForm): Promise { + const response = await request.post("/schools", data); + return response; +} + +// 更新学校信息 +export async function updateSchool(data: UpdateSchoolForm): Promise { + const response = await request.patch("/schools", data); + return response; +} + +// 删除学校信息 +export async function deleteSchool(): Promise { + return await request.delete("/schools"); +} + +export const schoolsApi = { + get: getSchool, + create: createSchool, + update: updateSchool, + delete: deleteSchool, +}; + diff --git a/java-frontend/src/api/students.ts b/java-frontend/src/api/students.ts new file mode 100644 index 0000000..30e249c --- /dev/null +++ b/java-frontend/src/api/students.ts @@ -0,0 +1,150 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Student { + id: number; + userId: number; + tenantId: number; + classId: number; + studentNo?: string; + phone?: string; + idCard?: string; + gender?: number; // 1-男,2-女 + birthDate?: string; + enrollmentDate?: string; + parentName?: string; + parentPhone?: string; + address?: string; + description?: string; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + user?: { + id: number; + username: string; + nickname: string; + email?: string; + avatar?: string; + validState: number; + }; + class?: { + id: number; + name: string; + code: string; + type: number; + grade?: { + id: number; + name: string; + code: string; + level: number; + }; + }; + interestClasses?: Array<{ + id: number; + class: { + id: number; + name: string; + code: string; + type: number; + grade?: { + id: number; + name: string; + code: string; + level: number; + }; + }; + }>; +} + +export interface CreateStudentForm { + username: string; + password: string; + nickname: string; + email?: string; + avatar?: string; + classId: number; + studentNo?: string; + phone?: string; + idCard?: string; + gender?: number; + birthDate?: string; + enrollmentDate?: string; + parentName?: string; + parentPhone?: string; + address?: string; + description?: string; + interestClassIds?: number[]; +} + +export interface UpdateStudentForm { + nickname?: string; + email?: string; + avatar?: string; + classId?: number; + studentNo?: string; + phone?: string; + idCard?: string; + gender?: number; + birthDate?: string; + enrollmentDate?: string; + parentName?: string; + parentPhone?: string; + address?: string; + description?: string; + validState?: number; + interestClassIds?: number[]; +} + +// 获取学生列表 +export async function getStudentsList( + params: PaginationParams & { classId?: number } +): Promise> { + const response = await request.get>("/students", { + params, + }); + return response; +} + +// 获取单个学生详情 +export async function getStudentDetail(id: number): Promise { + const response = await request.get(`/students/${id}`); + return response; +} + +// 根据用户ID获取学生信息 +export async function getStudentByUserId(userId: number): Promise { + const response = await request.get(`/students/user/${userId}`); + return response; +} + +// 创建学生 +export async function createStudent(data: CreateStudentForm): Promise { + const response = await request.post("/students", data); + return response; +} + +// 更新学生 +export async function updateStudent( + id: number, + data: UpdateStudentForm +): Promise { + const response = await request.patch(`/students/${id}`, data); + return response; +} + +// 删除学生 +export async function deleteStudent(id: number): Promise { + return await request.delete(`/students/${id}`); +} + +export const studentsApi = { + getList: getStudentsList, + getDetail: getStudentDetail, + getByUserId: getStudentByUserId, + create: createStudent, + update: updateStudent, + delete: deleteStudent, +}; + diff --git a/java-frontend/src/api/teachers.ts b/java-frontend/src/api/teachers.ts new file mode 100644 index 0000000..15b5b54 --- /dev/null +++ b/java-frontend/src/api/teachers.ts @@ -0,0 +1,123 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface Teacher { + id: number; + userId: number; + tenantId: number; + departmentId: number; + employeeNo?: string; + phone?: string; + idCard?: string; + gender?: number; // 1-男,2-女 + birthDate?: string; + hireDate?: string; + subject?: string; + title?: string; + description?: string; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + user?: { + id: number; + username: string; + nickname: string; + email?: string; + avatar?: string; + validState: number; + }; + department?: { + id: number; + name: string; + code: string; + }; +} + +export interface CreateTeacherForm { + username: string; + password: string; + nickname: string; + email?: string; + avatar?: string; + departmentId: number; + employeeNo?: string; + phone?: string; + idCard?: string; + gender?: number; + birthDate?: string; + hireDate?: string; + subject?: string; + title?: string; + description?: string; +} + +export interface UpdateTeacherForm { + nickname?: string; + email?: string; + avatar?: string; + departmentId?: number; + employeeNo?: string; + phone?: string; + idCard?: string; + gender?: number; + birthDate?: string; + hireDate?: string; + subject?: string; + title?: string; + description?: string; + validState?: number; +} + +// 获取教师列表 +export async function getTeachersList( + params: PaginationParams & { departmentId?: number; nickname?: string; username?: string } +): Promise> { + const response = await request.get>("/teachers", { + params, + }); + return response; +} + +// 获取单个教师详情 +export async function getTeacherDetail(id: number): Promise { + const response = await request.get(`/teachers/${id}`); + return response; +} + +// 根据用户ID获取教师信息 +export async function getTeacherByUserId(userId: number): Promise { + const response = await request.get(`/teachers/user/${userId}`); + return response; +} + +// 创建教师 +export async function createTeacher(data: CreateTeacherForm): Promise { + const response = await request.post("/teachers", data); + return response; +} + +// 更新教师 +export async function updateTeacher( + id: number, + data: UpdateTeacherForm +): Promise { + const response = await request.patch(`/teachers/${id}`, data); + return response; +} + +// 删除教师 +export async function deleteTeacher(id: number): Promise { + return await request.delete(`/teachers/${id}`); +} + +export const teachersApi = { + getList: getTeachersList, + getDetail: getTeacherDetail, + getByUserId: getTeacherByUserId, + create: createTeacher, + update: updateTeacher, + delete: deleteTeacher, +}; + diff --git a/java-frontend/src/api/tenants.ts b/java-frontend/src/api/tenants.ts new file mode 100644 index 0000000..9825f2b --- /dev/null +++ b/java-frontend/src/api/tenants.ts @@ -0,0 +1,98 @@ +import request from "@/utils/request"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; +import type { Menu } from "./menus"; + +export interface Tenant { + id: number; + name: string; + code: string; + domain?: string; + description?: string; + isSuper: number; + tenantType?: string; + validState: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + menus?: Array<{ + id: number; + menu: Menu; + }>; + _count?: { + users: number; + roles: number; + }; +} + +export interface CreateTenantForm { + name: string; + code: string; + domain?: string; + description?: string; + menuIds?: number[]; +} + +export interface UpdateTenantForm { + name?: string; + code?: string; + domain?: string; + description?: string; + validState?: number; + menuIds?: number[]; +} + +// 获取租户列表 +export async function getTenantsList( + params: PaginationParams +): Promise> { + const response = await request.get>( + "/tenants", + { + params, + } + ); + return response; +} + +// 获取单个租户详情 +export async function getTenantDetail(id: number): Promise { + const response = await request.get(`/tenants/${id}`); + return response; +} + +// 创建租户 +export async function createTenant(data: CreateTenantForm): Promise { + const response = await request.post("/tenants", data); + return response; +} + +// 更新租户 +export async function updateTenant( + id: number, + data: UpdateTenantForm +): Promise { + const response = await request.patch(`/tenants/${id}`, data); + return response; +} + +// 删除租户 +export async function deleteTenant(id: number): Promise { + return await request.delete(`/tenants/${id}`); +} + +// 获取租户的菜单树 +export async function getTenantMenus(id: number): Promise { + const response = await request.get(`/tenants/${id}/menus`); + return response; +} + +// 兼容性导出:保留 tenantsApi 对象 +export const tenantsApi = { + getList: getTenantsList, + getDetail: getTenantDetail, + create: createTenant, + update: updateTenant, + delete: deleteTenant, + getTenantMenus: getTenantMenus, +}; diff --git a/java-frontend/src/api/upload.ts b/java-frontend/src/api/upload.ts new file mode 100644 index 0000000..85113f6 --- /dev/null +++ b/java-frontend/src/api/upload.ts @@ -0,0 +1,33 @@ +import request from "@/utils/request"; + +export interface UploadResponse { + url: string; + filename: string; + originalname: string; + size: number; +} + +export const uploadApi = { + // 上传文件 + upload: async (formData: FormData): Promise => { + const response = await request.post( + "/upload", + formData, + { + headers: { + "Content-Type": "multipart/form-data", + }, + } + ); + return response; + }, +}; + +/** + * 上传单个文件 + */ +export async function uploadFile(file: File): Promise { + const formData = new FormData(); + formData.append("file", file); + return uploadApi.upload(formData); +} diff --git a/java-frontend/src/api/users.ts b/java-frontend/src/api/users.ts new file mode 100644 index 0000000..f0481c0 --- /dev/null +++ b/java-frontend/src/api/users.ts @@ -0,0 +1,172 @@ +import request from "@/utils/request"; +import type { PaginationResponse } from "@/types/api"; + +export interface UserQueryParams { + page?: number; + pageSize?: number; + keyword?: string; + userType?: "platform" | "org" | "judge" | "public"; + filterTenantId?: number; + userSource?: "admin_created" | "self_registered"; + status?: "enabled" | "disabled"; +} + +export interface UserTenant { + id: number; + name: string; + code: string; + tenantType: string; + isSuper: number; +} + +export interface User { + id: number; + username: string; + nickname: string; + email?: string; + phone?: string; + gender?: "male" | "female"; + avatar?: string; + status?: "enabled" | "disabled"; + userSource?: string; + city?: string; + birthday?: string; + organization?: string; + tenantId?: number; + validState?: number; + creator?: number; + modifier?: number; + createTime?: string; + modifyTime?: string; + tenant?: UserTenant; + roles?: Array<{ + id: number; + role: { + id: number; + name: string; + code: string; + }; + }>; + _count?: { + children: number; + contestRegistrations: number; + }; + // 详情接口返回 + children?: Array<{ + id: number; + name: string; + gender?: string; + birthday?: string; + grade?: string; + city?: string; + schoolName?: string; + }>; + contestRegistrations?: Array<{ + id: number; + registrationState: string; + participantType: string; + createTime: string; + contest: { id: number; contestName: string; contestState: string }; + child?: { id: number; name: string }; + }>; + contestJudges?: Array<{ + contest: { + id: number; + contestName: string; + status: string; + }; + }>; +} + +export interface UserStats { + total: number; + platform: number; + org: number; + judge: number; + public: number; +} + +export interface CreateUserForm { + username: string; + password: string; + nickname: string; + email?: string; + phone?: string; + gender?: "male" | "female"; + avatar?: string; + status?: "enabled" | "disabled"; + roleIds?: number[]; +} + +export interface UpdateUserForm { + username?: string; + password?: string; + nickname?: string; + email?: string; + phone?: string; + gender?: "male" | "female"; + avatar?: string; + status?: "enabled" | "disabled"; + roleIds?: number[]; +} + +// 获取用户列表 +export async function getUsersList( + params: UserQueryParams +): Promise> { + const response = await request.get>("/users", { + params, + }); + return response; +} + +// 获取用户统计 +export async function getUserStats(): Promise { + const response = await request.get("/users/stats"); + return response; +} + +// 获取单个用户详情 +export async function getUserDetail(id: number): Promise { + const response = await request.get(`/users/${id}`); + return response; +} + +// 创建用户 +export async function createUser(data: CreateUserForm): Promise { + const response = await request.post("/users", data); + return response; +} + +// 更新用户 +export async function updateUser( + id: number, + data: UpdateUserForm +): Promise { + const response = await request.patch(`/users/${id}`, data); + return response; +} + +// 切换用户状态 +export async function updateUserStatus( + id: number, + status: "enabled" | "disabled" +): Promise { + await request.patch(`/users/${id}/status`, { status }); +} + +// 删除用户 +export async function deleteUser(id: number): Promise { + return await request.delete(`/users/${id}`); +} + +// 兼容性导出:保留 usersApi 对象 +export const usersApi = { + getList: getUsersList, + getStats: getUserStats, + getDetail: getUserDetail, + create: createUser, + update: updateUser, + updateStatus: updateUserStatus, + delete: deleteUser, +}; diff --git a/java-frontend/src/assets/images/logo-icon.png b/java-frontend/src/assets/images/logo-icon.png new file mode 100644 index 0000000..4369389 Binary files /dev/null and b/java-frontend/src/assets/images/logo-icon.png differ diff --git a/java-frontend/src/assets/images/logo.png b/java-frontend/src/assets/images/logo.png new file mode 100644 index 0000000..4369389 Binary files /dev/null and b/java-frontend/src/assets/images/logo.png differ diff --git a/java-frontend/src/components/ModelViewer.vue b/java-frontend/src/components/ModelViewer.vue new file mode 100644 index 0000000..e5bbaec --- /dev/null +++ b/java-frontend/src/components/ModelViewer.vue @@ -0,0 +1,45 @@ + + + diff --git a/java-frontend/src/components/RichTextEditor.vue b/java-frontend/src/components/RichTextEditor.vue new file mode 100644 index 0000000..c9f2c0b --- /dev/null +++ b/java-frontend/src/components/RichTextEditor.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/java-frontend/src/composables/useListRequest.ts b/java-frontend/src/composables/useListRequest.ts new file mode 100644 index 0000000..7927be6 --- /dev/null +++ b/java-frontend/src/composables/useListRequest.ts @@ -0,0 +1,118 @@ +import { ref, reactive, type Ref } from "vue"; +import { message } from "ant-design-vue"; +import type { TableProps } from "ant-design-vue"; +import type { PaginationParams, PaginationResponse } from "@/types/api"; + +export interface UseListRequestOptions< + T, + P extends Record = Record, +> { + // 请求函数 + requestFn: (params: PaginationParams & P) => Promise>; + // 默认搜索参数 + defaultSearchParams?: P; + // 默认分页大小 + defaultPageSize?: number; + // 错误提示信息 + errorMessage?: string; + // 是否在挂载时自动加载 + immediate?: boolean; +} + +export function useListRequest< + T, + P extends Record = Record, +>(options: UseListRequestOptions) { + const { + requestFn, + defaultSearchParams = {} as P, + defaultPageSize = 10, + errorMessage = "获取列表失败", + immediate = true, + } = options; + + // 加载状态 + const loading = ref(false); + + // 数据源 + const dataSource = ref([]) as Ref; + + // 分页信息 + const pagination = reactive({ + current: 1, + pageSize: defaultPageSize, + total: 0, + }); + + // 搜索参数 + const searchParams = reactive

({ ...defaultSearchParams } as P); + + // 获取列表数据 + const fetchList = async () => { + loading.value = true; + try { + const params = { + page: pagination.current, + pageSize: pagination.pageSize, + ...searchParams, + } as PaginationParams & P; + + const response = await requestFn(params); + dataSource.value = response.list; + pagination.total = response.total; + } catch (error) { + message.error(errorMessage); + console.error("List request error:", error); + } finally { + loading.value = false; + } + }; + + // 重置搜索并刷新 + const resetSearch = () => { + Object.assign(searchParams, defaultSearchParams); + pagination.current = 1; + fetchList(); + }; + + // 搜索 + const search = (params?: Partial

) => { + if (params) { + Object.assign(searchParams, params); + } + pagination.current = 1; + fetchList(); + }; + + // 刷新当前页 + const refresh = () => { + fetchList(); + }; + + // 表格分页变化处理 + const handleTableChange: TableProps["onChange"] = (pag) => { + pagination.current = pag.current || 1; + pagination.pageSize = pag.pageSize || defaultPageSize; + fetchList(); + }; + + // 如果 immediate 为 true,在组合函数被调用时自动加载 + if (immediate) { + fetchList(); + } + + return { + // 状态 + loading, + dataSource, + pagination, + searchParams, + + // 方法 + fetchList, + resetSearch, + search, + refresh, + handleTableChange, + }; +} diff --git a/java-frontend/src/composables/useSimpleListRequest.ts b/java-frontend/src/composables/useSimpleListRequest.ts new file mode 100644 index 0000000..7484111 --- /dev/null +++ b/java-frontend/src/composables/useSimpleListRequest.ts @@ -0,0 +1,62 @@ +import { ref, type Ref } from "vue"; +import { message } from "ant-design-vue"; + +export interface UseSimpleListRequestOptions { + // 请求函数(返回数组,不是分页响应) + requestFn: () => Promise; + // 错误提示信息 + errorMessage?: string; + // 是否在挂载时自动加载 + immediate?: boolean; +} + +export function useSimpleListRequest( + options: UseSimpleListRequestOptions +) { + const { + requestFn, + errorMessage = "获取列表失败", + immediate = true, + } = options; + + // 加载状态 + const loading = ref(false); + + // 数据源 + const dataSource = ref([]) as Ref; + + // 获取列表数据 + const fetchList = async () => { + loading.value = true; + try { + const response = await requestFn(); + dataSource.value = response; + } catch (error) { + message.error(errorMessage); + console.error("List request error:", error); + } finally { + loading.value = false; + } + }; + + // 刷新列表 + const refresh = () => { + fetchList(); + }; + + // 如果 immediate 为 true,在组合函数被调用时自动加载 + if (immediate) { + fetchList(); + } + + return { + // 状态 + loading, + dataSource, + + // 方法 + fetchList, + refresh, + }; +} + diff --git a/java-frontend/src/directives/permission.ts b/java-frontend/src/directives/permission.ts new file mode 100644 index 0000000..195cc7a --- /dev/null +++ b/java-frontend/src/directives/permission.ts @@ -0,0 +1,146 @@ +import type { App, DirectiveBinding } from "vue"; +import { useAuthStore } from "@/stores/auth"; + +/** + * 权限指令配置 + */ +interface PermissionDirectiveValue { + // 权限码或权限码数组 + permission: string | string[]; + // 是否隐藏元素(默认 false,即禁用) + hide?: boolean; + // 是否需要所有权限(默认 false,即任一权限即可) + all?: boolean; +} + +/** + * 检查权限 + */ +function checkPermission( + value: string | string[] | PermissionDirectiveValue, + all: boolean = false +): boolean { + // 在指令中,我们需要通过 getCurrentInstance 获取 store + // 但更好的方式是直接导入 store,因为 Pinia store 是全局的 + const authStore = useAuthStore(); + + // 如果值是字符串或数组,直接检查权限 + if (typeof value === "string") { + return authStore.hasPermission(value); + } + + if (Array.isArray(value)) { + return all + ? value.every((perm) => authStore.hasPermission(perm)) + : authStore.hasAnyPermission(value); + } + + // 如果是对象配置 + if (typeof value === "object" && value !== null) { + const { permission, all: needAll = false } = value as PermissionDirectiveValue; + if (typeof permission === "string") { + return authStore.hasPermission(permission); + } + if (Array.isArray(permission)) { + return needAll + ? permission.every((perm) => authStore.hasPermission(perm)) + : authStore.hasAnyPermission(permission); + } + } + + return false; +} + +/** + * 处理元素权限 + */ +function handlePermission( + el: HTMLElement, + binding: DirectiveBinding +) { + const { value, modifiers } = binding; + + // 如果没有值,默认允许 + if (!value) { + return; + } + + // 检查是否需要所有权限 + const needAll = modifiers.all || false; + + // 解析配置 + let config: PermissionDirectiveValue; + if (typeof value === "string" || Array.isArray(value)) { + config = { + permission: value, + hide: modifiers.hide || false, + all: needAll, + }; + } else { + config = { + permission: value.permission, + hide: value.hide ?? modifiers.hide ?? false, + all: value.all ?? needAll, + }; + } + + const hasPermission = checkPermission(config.permission, config.all); + + if (config.hide) { + // 隐藏元素 + if (hasPermission) { + el.style.display = ""; + el.removeAttribute("data-permission-hidden"); + } else { + el.style.display = "none"; + el.setAttribute("data-permission-hidden", "true"); + } + } else { + // 禁用元素(默认行为) + if (hasPermission) { + // 恢复元素状态 + if (el instanceof HTMLButtonElement || el instanceof HTMLInputElement) { + el.disabled = false; + el.classList.remove("permission-disabled"); + } else { + el.style.pointerEvents = ""; + el.style.opacity = ""; + el.classList.remove("permission-disabled"); + } + el.removeAttribute("data-permission-disabled"); + } else { + // 禁用元素 + if (el instanceof HTMLButtonElement || el instanceof HTMLInputElement) { + el.disabled = true; + el.classList.add("permission-disabled"); + } else { + el.style.pointerEvents = "none"; + el.style.opacity = "0.6"; + el.classList.add("permission-disabled"); + } + el.setAttribute("data-permission-disabled", "true"); + } + } +} + +/** + * 权限指令 + */ +const permissionDirective = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + handlePermission(el, binding); + }, + updated(el: HTMLElement, binding: DirectiveBinding) { + handlePermission(el, binding); + }, +}; + +/** + * 注册权限指令 + */ +export function setupPermissionDirective(app: App) { + app.directive("permission", permissionDirective); +} + +export default permissionDirective; + diff --git a/java-frontend/src/layouts/BasicLayout.vue b/java-frontend/src/layouts/BasicLayout.vue new file mode 100644 index 0000000..d849147 --- /dev/null +++ b/java-frontend/src/layouts/BasicLayout.vue @@ -0,0 +1,681 @@ + + + + + diff --git a/java-frontend/src/layouts/EmptyLayout.vue b/java-frontend/src/layouts/EmptyLayout.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/java-frontend/src/layouts/EmptyLayout.vue @@ -0,0 +1,3 @@ + diff --git a/java-frontend/src/layouts/PublicLayout.vue b/java-frontend/src/layouts/PublicLayout.vue new file mode 100644 index 0000000..956721b --- /dev/null +++ b/java-frontend/src/layouts/PublicLayout.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/java-frontend/src/main.ts b/java-frontend/src/main.ts new file mode 100644 index 0000000..e609d68 --- /dev/null +++ b/java-frontend/src/main.ts @@ -0,0 +1,27 @@ +import { createApp } from "vue" +import { createPinia } from "pinia" +import Antd from "ant-design-vue" +import "ant-design-vue/dist/reset.css" +import "./styles/global.scss" +import "./styles/theme.scss" +import App from "./App.vue" +import router from "./router" +import { useAuthStore } from "./stores/auth" +import { setupPermissionDirective } from "./directives/permission" + +const app = createApp(App) +const pinia = createPinia() + +app.use(pinia) +app.use(router) +app.use(Antd) + +// 注册权限指令 +setupPermissionDirective(app) + +// 应用启动时初始化认证状态 +// 如果有 token,自动获取用户信息 +const authStore = useAuthStore() +authStore.initAuth().finally(() => { + app.mount("#app") +}) diff --git a/java-frontend/src/router/index.ts b/java-frontend/src/router/index.ts new file mode 100644 index 0000000..49f4be0 --- /dev/null +++ b/java-frontend/src/router/index.ts @@ -0,0 +1,890 @@ +import { createRouter, createWebHistory } from "vue-router" +import type { RouteRecordRaw } from "vue-router" +import { nextTick } from "vue" +import { useAuthStore } from "@/stores/auth" +import { convertMenusToRoutes } from "@/utils/menu" +import "@/types/router" + +// 基础路由(不需要动态加载的) +const baseRoutes: RouteRecordRaw[] = [ + { + path: "/:tenantCode/login", + name: "Login", + component: () => import("@/views/auth/Login.vue"), + meta: { requiresAuth: false }, + }, + { + path: "/login", + name: "LoginFallback", + component: () => import("@/views/auth/Login.vue"), + meta: { requiresAuth: false }, + }, + // ========== 公众端路由 ========== + { + path: "/p/login", + name: "PublicLogin", + component: () => import("@/views/public/Login.vue"), + meta: { requiresAuth: false }, + }, + { + path: "/p", + name: "PublicMain", + component: () => import("@/layouts/PublicLayout.vue"), + meta: { requiresAuth: false }, + children: [ + { + path: "", + redirect: "/p/gallery", + }, + { + path: "gallery", + name: "PublicGallery", + component: () => import("@/views/public/Gallery.vue"), + meta: { title: "作品广场" }, + }, + { + path: "activities", + name: "PublicActivities", + component: () => import("@/views/public/Activities.vue"), + meta: { title: "活动大厅" }, + }, + { + path: "activities/:id", + name: "PublicActivityDetail", + component: () => import("@/views/public/ActivityDetail.vue"), + meta: { title: "活动详情" }, + }, + { + path: "mine", + name: "PublicMine", + component: () => import("@/views/public/mine/Index.vue"), + meta: { title: "个人中心" }, + }, + { + path: "mine/registrations", + name: "PublicMyRegistrations", + component: () => import("@/views/public/mine/Registrations.vue"), + meta: { title: "我的报名" }, + }, + { + path: "mine/works", + name: "PublicMyWorks", + component: () => import("@/views/public/mine/Works.vue"), + meta: { title: "我的作品" }, + }, + { + path: "mine/children", + name: "PublicMyChildren", + component: () => import("@/views/public/mine/Children.vue"), + meta: { title: "子女账号" }, + }, + // ========== 创作与作品库 ========== + { + path: "create", + name: "PublicCreate", + component: () => import("@/views/public/create/Index.vue"), + meta: { title: "绘本创作" }, + }, + { + path: "create/generating/:id", + name: "PublicCreating", + component: () => import("@/views/public/create/Generating.vue"), + meta: { title: "生成中" }, + }, + { + path: "works", + name: "PublicWorksList", + component: () => import("@/views/public/works/Index.vue"), + meta: { title: "我的作品库" }, + }, + { + path: "works/:id", + name: "PublicWorkDetail", + component: () => import("@/views/public/works/Detail.vue"), + meta: { title: "作品详情" }, + }, + { + path: "works/:id/publish", + name: "PublicWorkPublish", + component: () => import("@/views/public/works/Publish.vue"), + meta: { title: "发布作品" }, + }, + ], + }, + // ========== 管理端路由 ========== + { + path: "/:tenantCode", + name: "Main", + component: () => import("@/layouts/BasicLayout.vue"), + // 不设置固定redirect,由路由守卫根据用户菜单动态跳转到第一个可见菜单 + meta: {}, + children: [ + // 创建活动路由(不需要在菜单中显示) + { + path: "contests/create", + name: "ContestsCreate", + component: () => import("@/views/contests/Create.vue"), + meta: { + title: "创建活动", + requiresAuth: true, + permissions: ["contest:create"], + }, + }, + // 超管活动详情路由 + { + path: "contests/:id/overview", + name: "SuperContestOverview", + component: () => import("@/views/contests/SuperDetail.vue"), + meta: { + title: "活动详情", + requiresAuth: true, + }, + }, + // 活动详情路由(不需要在菜单中显示) + { + path: "contests/:id", + name: "ContestsDetail", + component: () => import("@/views/contests/Detail.vue"), + meta: { + title: "活动详情", + requiresAuth: true, + permissions: ["contest:read", "activity:read"], + }, + }, + // 编辑活动路由(不需要在菜单中显示) + { + path: "contests/:id/edit", + name: "ContestsEdit", + component: () => import("@/views/contests/Create.vue"), + meta: { + title: "编辑活动", + requiresAuth: true, + permissions: ["contest:update"], + }, + }, + // 活动评委管理路由(不需要在菜单中显示) + { + path: "contests/:id/judges", + name: "ContestsJudges", + component: () => import("@/views/contests/judges/Index.vue"), + meta: { + title: "评委管理", + requiresAuth: true, + permissions: ["contest:read"], + }, + }, + // 个人参与报名路由 + { + path: "contests/:id/register/individual", + name: "ContestsRegisterIndividual", + component: () => import("@/views/contests/RegisterIndividual.vue"), + meta: { + title: "活动报名(个人参与)", + requiresAuth: true, + }, + }, + // 团队参与报名路由 + { + path: "contests/:id/register/team", + name: "ContestsRegisterTeam", + component: () => import("@/views/contests/RegisterTeam.vue"), + meta: { + title: "活动报名(团队参与)", + requiresAuth: true, + }, + }, + // 报名管理列表路由 + { + path: "contests/registrations", + name: "ContestsRegistrations", + component: () => import("@/views/contests/registrations/Index.vue"), + meta: { + title: "报名管理", + requiresAuth: true, + permissions: ["registration:read", "contest:read"], + }, + }, + // 报名记录路由 + { + path: "contests/registrations/:id/records", + name: "RegistrationRecords", + component: () => import("@/views/contests/registrations/Records.vue"), + meta: { + title: "报名记录", + requiresAuth: true, + permissions: ["registration:read", "contest:read"], + }, + }, + // 评委评审详情路由(从评审任务列表进入) + { + path: "activities/review/:id", + name: "ActivitiesReviewDetail", + component: () => import("@/views/activities/ReviewDetail.vue"), + meta: { + title: "评审工作台", + requiresAuth: true, + permissions: ["review:score"], + }, + }, + // 评审进度详情路由 + { + path: "contests/reviews/:id/progress", + name: "ReviewProgressDetail", + component: () => import("@/views/contests/reviews/ProgressDetail.vue"), + meta: { + title: "评审进度详情", + requiresAuth: true, + }, + }, + // 成果发布详情路由 + { + path: "contests/results/:id", + name: "ContestsResultsDetail", + component: () => import("@/views/contests/results/Detail.vue"), + meta: { + title: "成果发布详情", + requiresAuth: true, + }, + }, + // 参赛作品详情列表路由 + { + path: "contests/works/:id/list", + name: "WorksDetail", + component: () => import("@/views/contests/works/WorksDetail.vue"), + meta: { + title: "参赛作品详情", + requiresAuth: true, + permissions: ["work:read"], + }, + }, + // 作业提交记录路由 + { + path: "homework/submissions", + name: "HomeworkSubmissions", + component: () => import("@/views/homework/Submissions.vue"), + meta: { + title: "作业提交记录", + requiresAuth: true, + permissions: ["homework:read"], + }, + }, + // 学生作业详情路由 + { + path: "homework/detail/:id", + name: "HomeworkDetail", + component: () => import("@/views/homework/StudentDetail.vue"), + meta: { + title: "作业详情", + requiresAuth: true, + permissions: ["homework:read"], + }, + }, + // 教师我的指导路由 + { + path: "student-activities/guidance", + name: "TeacherGuidance", + component: () => import("@/views/contests/Guidance.vue"), + meta: { + title: "我的指导", + requiresAuth: true, + permissions: ["activity:read"], + }, + }, + // 评委评审详情页 + { + path: "activities/review/:id", + name: "ReviewDetail", + component: () => import("@/views/activities/ReviewDetail.vue"), + meta: { + title: "作品评审", + requiresAuth: true, + }, + }, + // 预设评语页面 + { + path: "activities/preset-comments", + name: "PresetComments", + component: () => import("@/views/activities/PresetComments.vue"), + meta: { + title: "预设评语", + requiresAuth: true, + }, + }, + // 3D建模实验室路由(工作台模块下) + { + path: "workbench/3d-lab", + name: "3DModelingLab", + component: () => import("@/views/workbench/ai-3d/Index.vue"), + meta: { + title: "3D建模实验室", + requiresAuth: true, + hideSidebar: true, + }, + }, + // 3D模型生成页面 + { + path: "workbench/3d-lab/generate/:taskId", + name: "AI3DGenerate", + component: () => import("@/views/workbench/ai-3d/Generate.vue"), + meta: { + title: "3D模型生成", + requiresAuth: true, + hideSidebar: true, + }, + }, + // 3D创作历史页面 + { + path: "workbench/3d-lab/history", + name: "AI3DHistory", + component: () => import("@/views/workbench/ai-3d/History.vue"), + meta: { + title: "创作历史", + requiresAuth: true, + hideSidebar: true, + }, + }, + // 3D模型预览页面 + { + path: "workbench/model-viewer", + name: "ModelViewer", + component: () => import("@/views/model/ModelViewer.vue"), + meta: { + title: "3D模型预览", + requiresAuth: true, + hideSidebar: true, + }, + }, + // 动态路由将在这里添加 + ], + }, + { + path: "/403", + name: "Forbidden", + component: () => import("@/views/error/403.vue"), + meta: { requiresAuth: false }, + }, + { + path: "/:pathMatch(.*)*", + name: "NotFound", + component: () => import("@/views/error/404.vue"), + }, +] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: baseRoutes, +}) + +// 标记是否已经添加了动态路由 +let dynamicRoutesAdded = false +// 保存已添加的路由名称,用于清理 +let addedRouteNames: string[] = [] +// 保存上次的菜单数据,用于检测变化 +let lastMenusHash: string = "" + +/** + * 重置动态路由标记(用于登出或切换用户时) + */ +export function resetDynamicRoutes(): void { + dynamicRoutesAdded = false + addedRouteNames = [] + lastMenusHash = "" +} + +/** + * 添加动态路由 + * @returns Promise,当路由添加完成并生效后 resolve + */ +export async function addDynamicRoutes(): Promise { + const authStore = useAuthStore() + if (!authStore.menus || authStore.menus.length === 0) { + // 如果没有菜单,重置标记 + if (dynamicRoutesAdded) { + resetDynamicRoutes() + } + return + } + + // 计算菜单数据的哈希值,用于检测变化 + const menusHash = JSON.stringify( + authStore.menus.map((m) => ({ id: m.id, path: m.path })) + ) + + // 如果菜单数据没有变化且已经添加过路由,直接返回 + if (dynamicRoutesAdded && menusHash === lastMenusHash) { + return + } + + // 如果已经添加过路由,先移除旧路由 + if (dynamicRoutesAdded && addedRouteNames.length > 0) { + addedRouteNames.forEach((routeName) => { + if (router.hasRoute(routeName)) { + router.removeRoute(routeName) + } + }) + addedRouteNames = [] + } + + // 将菜单转换为路由 + const dynamicRoutes = convertMenusToRoutes(authStore.menus) + + if (dynamicRoutes.length === 0) { + return + } + + // 添加动态路由到根路由下 + dynamicRoutes.forEach((route) => { + router.addRoute("Main", route) + if (route.name) { + addedRouteNames.push(route.name as string) + } + }) + + dynamicRoutesAdded = true + lastMenusHash = menusHash + + // 等待多个 tick,确保路由已完全注册 + await nextTick() + await nextTick() + await nextTick() + + // 额外等待一小段时间,确保路由系统完全更新 + await new Promise((resolve) => setTimeout(resolve, 50)) +} + +/** + * 从路径中提取租户编码 + */ +function extractTenantCodeFromPath(path: string): string | null { + const match = path.match(/^\/([^/]+)/) + return match ? match[1] : null +} + +/** + * 构建带租户编码的路径 + */ +function buildPathWithTenantCode(tenantCode: string, path: string): string { + // 如果路径已经包含租户编码,直接返回 + if (path.startsWith(`/${tenantCode}/`)) { + return path + } + // 移除开头的斜杠(如果有) + const cleanPath = path.startsWith("/") ? path.slice(1) : path + // 如果路径是根路径,返回租户编码根路径(路由守卫会处理跳转到第一个菜单) + if (cleanPath === "" || cleanPath === tenantCode) { + return `/${tenantCode}` + } + return `/${tenantCode}/${cleanPath}` +} + +router.beforeEach(async (to, _from, next) => { + // 公众端路由不走管理端认证逻辑,直接放行 + if (to.path.startsWith("/p/") || to.path === "/p") { + next() + return + } + + const authStore = useAuthStore() + + // 从URL中提取租户编码 + const tenantCodeFromUrl = extractTenantCodeFromPath(to.path) + + // 如果 token 存在但用户信息不存在,先获取用户信息 + if (authStore.token && !authStore.user) { + try { + const userInfo = await authStore.fetchUserInfo() + + // 如果获取用户信息失败或用户信息为空,跳转到登录页 + if (!userInfo) { + authStore.logout() + const tenantCode = + tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + return + } + + // 获取用户信息后,检查租户编码一致性 + const userTenantCode = userInfo?.tenantCode + if (userTenantCode) { + // 如果URL中的租户编码与用户信息不一致,更正URL + if (tenantCodeFromUrl && tenantCodeFromUrl !== userTenantCode) { + const correctedPath = buildPathWithTenantCode( + userTenantCode, + to.path.replace(`/${tenantCodeFromUrl}`, "") + ) + next({ path: correctedPath, query: to.query, replace: true }) + return + } + // 如果URL中没有租户编码,添加租户编码 + if (!tenantCodeFromUrl) { + const correctedPath = buildPathWithTenantCode(userTenantCode, to.path) + next({ path: correctedPath, query: to.query, replace: true }) + return + } + } + // 获取用户信息后,添加动态路由并等待生效 + await addDynamicRoutes() + // 保存原始目标路径 + const targetPath = to.fullPath + // 路由已生效,重新解析目标路由 + const resolved = router.resolve(targetPath) + + // 如果目标是租户根路径(如 /judge、/super),直接跳转到第一个菜单 + const isRootPath = to.matched.length === 1 && to.matched[0].name === "Main" + if (isRootPath && authStore.menus?.length) { + const findFirst = (menus: any[]): string | null => { + for (const m of menus) { + if (m.path && m.component) return m.path.startsWith("/") ? m.path.slice(1) : m.path + if (m.children?.length) { const c = findFirst(m.children); if (c) return c } + } + return null + } + const first = findFirst(authStore.menus) + if (first) { + const tc = tenantCodeFromUrl || authStore.user?.tenantCode + if (tc) { next({ path: `/${tc}/${first}`, replace: true }); return } + } + } + + // 如果解析后的路由不是404,说明路由存在,重新导航 + if (resolved.name !== "NotFound") { + next({ path: targetPath, replace: true }) + } else { + // 如果路由不存在,尝试重定向到用户第一个菜单 + if (authStore.menus && authStore.menus.length > 0) { + const findFirstMenuPath = (menus: any[]): string | null => { + for (const menu of menus) { + if (menu.path && menu.component) { + // 移除开头的斜杠 + return menu.path.startsWith("/") + ? menu.path.slice(1) + : menu.path + } + if (menu.children && menu.children.length > 0) { + const childPath = findFirstMenuPath(menu.children) + if (childPath) return childPath + } + } + return null + } + + const firstMenuPath = findFirstMenuPath(authStore.menus) + if (firstMenuPath) { + const user = authStore.user as { tenantCode?: string } | null + const userTenantCode = user?.tenantCode + const tenantCode = + tenantCodeFromUrl || + extractTenantCodeFromPath(to.path) || + userTenantCode + if (tenantCode) { + next({ path: `/${tenantCode}/${firstMenuPath}`, replace: true }) + return + } + } + } + + // 如果路由不存在,但需要认证,跳转到登录页(而不是404) + if (to.meta.requiresAuth === false) { + // 路由确实不存在,允许继续(会显示404页面) + next() + } else { + const tenantCode = + tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + } + } + return + } catch (error) { + // 获取失败,清除 token 并跳转到登录页 + console.error("获取用户信息失败:", error) + authStore.logout() + const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + return + } + } + + // 如果 token 不存在,但需要认证,跳转到登录页 + if (!authStore.token && to.meta.requiresAuth !== false) { + const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + return + } + + // 如果已登录,检查租户编码一致性 + if (authStore.isAuthenticated && authStore.user) { + const userTenantCode = authStore.user.tenantCode + if (userTenantCode) { + // 如果URL中的租户编码与用户信息不一致,更正URL + if (tenantCodeFromUrl && tenantCodeFromUrl !== userTenantCode) { + const correctedPath = buildPathWithTenantCode( + userTenantCode, + to.path.replace(`/${tenantCodeFromUrl}`, "") + ) + next({ path: correctedPath, query: to.query, replace: true }) + return + } + // 如果URL中没有租户编码,添加租户编码(排除不需要认证的特殊路由) + const skipTenantCodePaths = ["/login", "/403"] + const shouldSkipTenantCode = skipTenantCodePaths.some(p => to.path.startsWith(p)) + if (!tenantCodeFromUrl && !shouldSkipTenantCode) { + const correctedPath = buildPathWithTenantCode(userTenantCode, to.path) + next({ path: correctedPath, query: to.query, replace: true }) + return + } + } + } + + // 如果已登录且有菜单数据,添加或更新动态路由 + if (authStore.isAuthenticated && authStore.menus.length > 0) { + // 保存添加路由前的状态 + const wasRoutesAdded = dynamicRoutesAdded + + // 添加或更新动态路由 + await addDynamicRoutes() + + // 如果这是第一次添加路由,需要重新导航 + if (!wasRoutesAdded && dynamicRoutesAdded) { + // 等待路由完全生效 + await nextTick() + await nextTick() + + // 保存原始目标路径 + const targetPath = to.fullPath + // 路由已生效,重新解析目标路由 + const resolved = router.resolve(targetPath) + + // 如果访问的是主路由,重定向到第一个菜单 + const isMainRoute = to.name === "Main" + + console.log('Route guard debug:', { + targetPath, + resolvedName: resolved.name, + resolvedPath: resolved.path, + isMainRoute, + toName: to.name, + toPath: to.path, + }) + + // 如果解析后的路由不是404,说明路由存在,重新导航 + if (resolved.name !== "NotFound" && !isMainRoute) { + next({ path: targetPath, replace: true }) + return + } + + // 如果路由不存在或是主路由,尝试重定向到用户第一个菜单 + if (authStore.menus && authStore.menus.length > 0) { + const findFirstMenuPath = (menus: any[]): string | null => { + for (const menu of menus) { + if (menu.path && menu.component) { + // 移除开头的斜杠 + return menu.path.startsWith("/") ? menu.path.slice(1) : menu.path + } + if (menu.children && menu.children.length > 0) { + const childPath = findFirstMenuPath(menu.children) + if (childPath) return childPath + } + } + return null + } + + const firstMenuPath = findFirstMenuPath(authStore.menus) + if (firstMenuPath) { + const userTenantCode = authStore.user + ? (authStore.user.tenantCode as string | undefined) + : undefined + const tenantCode = + tenantCodeFromUrl || + extractTenantCodeFromPath(to.path) || + userTenantCode + if (tenantCode) { + // 再次等待,确保路由完全注册 + await nextTick() + next({ path: `/${tenantCode}/${firstMenuPath}`, replace: true }) + return + } + } + } + + // 如果没有任何菜单,跳转到404页面 + const tenantCodeFor404 = + tenantCodeFromUrl || + extractTenantCodeFromPath(to.path) || + (authStore.user + ? (authStore.user.tenantCode as string | undefined) + : undefined) + if (tenantCodeFor404) { + next({ path: `/${tenantCodeFor404}/404`, replace: true }) + } else { + next({ name: "NotFound" }) + } + return + } + } + + // 如果已登录且有菜单,但路由已添加,检查当前路由是否存在 + if ( + authStore.isAuthenticated && + authStore.menus.length > 0 && + dynamicRoutesAdded + ) { + const resolved = router.resolve(to.fullPath) + // 如果访问的是 Main 路由(无具体子路径)或路由不存在,重定向到用户第一个菜单 + const isMainRouteWithoutChild = to.name === "Main" || to.matched.length === 1 && to.matched[0].name === "Main" + if ( + (resolved.name === "NotFound" || isMainRouteWithoutChild) && + to.name !== "Login" && + to.name !== "LoginFallback" + ) { + const findFirstMenuPath = (menus: any[]): string | null => { + for (const menu of menus) { + if (menu.path && menu.component) { + return menu.path.startsWith("/") ? menu.path.slice(1) : menu.path + } + if (menu.children && menu.children.length > 0) { + const childPath = findFirstMenuPath(menu.children) + if (childPath) return childPath + } + } + return null + } + + const firstMenuPath = findFirstMenuPath(authStore.menus) + if (firstMenuPath) { + const userTenantCode = authStore.user + ? (authStore.user.tenantCode as string | undefined) + : undefined + const tenantCode = + tenantCodeFromUrl || + extractTenantCodeFromPath(to.path) || + userTenantCode + if (tenantCode) { + next({ path: `/${tenantCode}/${firstMenuPath}`, replace: true }) + return + } + } + + // 如果没有任何菜单,跳转到404页面 + const tenantCodeFor404 = + tenantCodeFromUrl || + extractTenantCodeFromPath(to.path) || + (authStore.user + ? (authStore.user.tenantCode as string | undefined) + : undefined) + if (tenantCodeFor404) { + next({ path: `/${tenantCodeFor404}/404`, replace: true }) + return + } + } + } + + // 检查是否需要认证 + if (to.meta.requiresAuth !== false) { + // 如果没有 token,跳转到登录页 + if (!authStore.token) { + const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + return + } + // 如果有 token 但没有用户信息,跳转到登录页 + if (!authStore.user) { + const tenantCode = tenantCodeFromUrl || extractTenantCodeFromPath(to.path) + if (tenantCode) { + next({ + path: `/${tenantCode}/login`, + query: { redirect: to.fullPath }, + }) + } else { + next({ name: "LoginFallback", query: { redirect: to.fullPath } }) + } + return + } + } + + // 如果已登录,访问登录页则重定向到首页 + if ( + (to.name === "Login" || to.name === "LoginFallback") && + authStore.isAuthenticated + ) { + // 确保动态路由已添加并等待生效 + if (!dynamicRoutesAdded && authStore.menus.length > 0) { + await addDynamicRoutes() + } + // 重定向到带租户编码的根路径(路由守卫会处理跳转到第一个菜单) + const userTenantCode = authStore.user?.tenantCode || "default" + next({ path: `/${userTenantCode}` }) + return + } + + // 处理登录页面的租户编码 + if (to.name === "LoginFallback" && !tenantCodeFromUrl) { + // 如果访问的是 /login,但没有租户编码,检查是否有用户信息中的租户编码 + if (authStore.isAuthenticated && authStore.user?.tenantCode) { + const userTenantCode = authStore.user.tenantCode + next({ path: `/${userTenantCode}/login`, replace: true }) + return + } + // 如果没有租户编码,允许访问(会显示租户输入框) + next() + return + } + + // 检查角色权限 + const requiredRoles = to.meta.roles + if (requiredRoles && requiredRoles.length > 0) { + if (!authStore.hasAnyRole(requiredRoles)) { + // 没有所需角色,跳转到 403 页面 + next({ name: "Forbidden" }) + return + } + } + + // 检查权限 + const requiredPermissions = to.meta.permissions + if (requiredPermissions && requiredPermissions.length > 0) { + if (!authStore.hasAnyPermission(requiredPermissions)) { + // 没有所需权限,跳转到 403 页面 + next({ name: "Forbidden" }) + return + } + } + + next() +}) + +export default router diff --git a/java-frontend/src/stores/auth.ts b/java-frontend/src/stores/auth.ts new file mode 100644 index 0000000..5ff4ebc --- /dev/null +++ b/java-frontend/src/stores/auth.ts @@ -0,0 +1,163 @@ +import { defineStore } from "pinia" +import { ref, computed } from "vue" +import type { User, LoginForm } from "@/types/auth" +import { authApi } from "@/api/auth" +import { menusApi, type Menu } from "@/api/menus" +import { getToken, setToken, removeToken, getTenantCode } from "@/utils/auth" + +export const useAuthStore = defineStore("auth", () => { + const user = ref(null) + const token = ref(getToken() || "") + const loading = ref(false) + const menus = ref([]) + + const isAuthenticated = computed(() => !!token.value) + + // 获取当前用户的租户编码(优先从用户信息获取,其次从 URL 获取) + const tenantCode = computed(() => { + return user.value?.tenantCode || getTenantCode() + }) + + // 检查是否有指定角色 + const hasRole = (role: string): boolean => { + return user.value?.roles?.includes(role) ?? false + } + + // 检查是否为超级管理员 + const isSuperAdmin = (): boolean => { + return user.value?.roles?.includes('super_admin') ?? false + } + + // 检查是否有指定权限 + const hasPermission = (permission: string): boolean => { + // 超级管理员拥有所有权限 + if (isSuperAdmin()) return true + return user.value?.permissions?.includes(permission) ?? false + } + + // 检查是否有任一角色 + const hasAnyRole = (roles: string[]): boolean => { + if (!roles || roles.length === 0) return true + return roles.some((role) => hasRole(role)) + } + + // 检查是否有任一权限 + const hasAnyPermission = (permissions: string[]): boolean => { + if (!permissions || permissions.length === 0) return true + // 超级管理员拥有所有权限 + if (isSuperAdmin()) return true + return permissions.some((perm) => hasPermission(perm)) + } + + const login = async (form: LoginForm) => { + const response = await authApi.login(form) + token.value = response.token + user.value = response.user + + // 使用租户编码作为 cookie path(不再存储到 localStorage) + if (response.user.tenantCode) { + setToken(response.token, response.user.tenantCode) + } else { + setToken(response.token) + } + + // 登录后获取用户菜单 + await fetchUserMenus() + return response + } + + const logout = async () => { + try { + await authApi.logout() + } finally { + token.value = "" + // 删除 token cookie,使用当前用户的租户编码或 URL 中的租户编码 + const tenantCode = user.value?.tenantCode || getTenantCode() + removeToken(tenantCode || undefined) + user.value = null + menus.value = [] + // 重置动态路由标记 + if (typeof window !== "undefined") { + const { resetDynamicRoutes } = await import("@/router") + resetDynamicRoutes() + } + } + } + + const fetchUserInfo = async () => { + if (!token.value) { + throw new Error("未登录") + } + + try { + loading.value = true + const response = await authApi.getUserInfo() + user.value = response + // 获取用户菜单 + await fetchUserMenus() + return response + } catch (error) { + // 如果获取用户信息失败,清除 token + token.value = "" + user.value = null + menus.value = [] + removeToken() + throw error + } finally { + loading.value = false + } + } + + const fetchUserMenus = async () => { + if (!token.value) { + return + } + + try { + const userMenus = await menusApi.getUserMenus() + menus.value = userMenus + return userMenus + } catch (error) { + console.error("获取用户菜单失败:", error) + menus.value = [] + throw error + } + } + + const updateToken = (newToken: string) => { + token.value = newToken + // 使用当前用户的租户编码更新 token cookie + const tenantCode = user.value?.tenantCode || getTenantCode() + setToken(newToken, tenantCode || undefined) + } + + // 初始化:如果有 token 但没有用户信息,自动获取 + const initAuth = async () => { + if (token.value && !user.value) { + try { + await fetchUserInfo() + } catch (error) { + console.error("自动获取用户信息失败:", error) + } + } + } + + return { + user, + token, + loading, + menus, + isAuthenticated, + tenantCode, + hasRole, + hasPermission, + hasAnyRole, + hasAnyPermission, + login, + logout, + fetchUserInfo, + fetchUserMenus, + updateToken, + initAuth, + } +}) diff --git a/java-frontend/src/styles/global.scss b/java-frontend/src/styles/global.scss new file mode 100644 index 0000000..f55684b --- /dev/null +++ b/java-frontend/src/styles/global.scss @@ -0,0 +1,206 @@ +@use "./tailwind.scss"; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "PingFang SC", + "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #1e1b4b; +} + +#app { + min-height: 100vh; +} + +// ========== 全局 Ant Design 组件风格覆盖 ========== + +// 按钮 — 更圆润、更有活力 +.ant-btn { + border-radius: 10px !important; + font-weight: 600 !important; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important; + + &.ant-btn-primary { + box-shadow: 0 2px 8px rgba(99, 102, 241, 0.25) !important; + + &:hover { + box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35) !important; + transform: translateY(-1px); + } + + &:active { + transform: scale(0.98); + } + } +} + +// 卡片 — 柔和圆角 + 暖色阴影 +.ant-card { + border-radius: 16px !important; + border: 1px solid rgba(99, 102, 241, 0.08) !important; + box-shadow: 0 1px 3px rgba(99, 102, 241, 0.05), + 0 4px 12px rgba(99, 102, 241, 0.03) !important; + overflow: hidden; + + .ant-card-head { + border-bottom: 1px solid rgba(99, 102, 241, 0.08) !important; + font-weight: 600; + } +} + +// 表格 — 更柔和的色调 +.ant-table-wrapper { + .ant-table { + border-radius: 16px !important; + overflow: hidden; + } + + .ant-table-thead > tr > th { + background: #f5f3ff !important; + color: #6b7280 !important; + font-weight: 600 !important; + font-size: 13px !important; + border-bottom: 1px solid #e5e1f5 !important; + } + + .ant-table-tbody > tr { + transition: all 0.2s ease; + + &:hover > td { + background: #faf9fe !important; + } + + > td { + border-bottom: 1px solid #f0ecf9 !important; + } + } + + .ant-table-pagination { + margin: 16px 0 !important; + } +} + +// 标签 — 药丸形状 +.ant-tag { + border-radius: 20px !important; + border: none !important; + padding: 2px 12px !important; + font-weight: 500 !important; + font-size: 12px !important; +} + +// 输入框 / 选择器 — 更圆润 +.ant-input, +.ant-input-affix-wrapper, +.ant-select-selector, +.ant-picker { + border-radius: 10px !important; + border-color: #e5e1f5 !important; + + &:hover { + border-color: #818cf8 !important; + } + + &:focus, + &.ant-input-affix-wrapper-focused, + &.ant-select-focused { + border-color: #6366f1 !important; + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.08) !important; + } +} + +.ant-select-selector { + border-color: #e5e1f5 !important; +} + +.ant-select-focused .ant-select-selector { + border-color: #6366f1 !important; + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.08) !important; +} + +// 模态框 — 更大圆角 +.ant-modal-content { + border-radius: 16px !important; + overflow: hidden; +} + +// 抽屉 +.ant-drawer-content { + border-radius: 16px 0 0 16px !important; +} + +// 分页器 — 圆润 +.ant-pagination { + .ant-pagination-item { + border-radius: 8px !important; + border-color: #e5e1f5 !important; + + &:hover { + border-color: #6366f1 !important; + } + + &.ant-pagination-item-active { + background: #6366f1 !important; + border-color: #6366f1 !important; + + a { + color: #fff !important; + } + } + } + + .ant-pagination-prev, + .ant-pagination-next { + .ant-pagination-item-link { + border-radius: 8px !important; + border-color: #e5e1f5 !important; + } + } +} + +// 面包屑 +.ant-breadcrumb { + font-size: 14px; + color: #9ca3af; +} + +// 消息提示 — 圆润 +.ant-message-notice-content { + border-radius: 12px !important; + box-shadow: 0 4px 16px rgba(99, 102, 241, 0.12), + 0 8px 32px rgba(99, 102, 241, 0.06) !important; +} + +// Tooltip +.ant-tooltip-inner { + border-radius: 10px !important; +} + +// 下拉菜单 +.ant-dropdown-menu { + border-radius: 12px !important; + box-shadow: 0 4px 16px rgba(99, 102, 241, 0.1), + 0 8px 32px rgba(99, 102, 241, 0.06) !important; + padding: 6px !important; + + .ant-dropdown-menu-item { + border-radius: 8px !important; + } +} + +// Tabs +.ant-tabs .ant-tabs-tab { + border-radius: 10px !important; +} + +// 表单 +.ant-form-item-label > label { + font-weight: 500 !important; + color: #374151 !important; +} diff --git a/java-frontend/src/styles/tailwind.scss b/java-frontend/src/styles/tailwind.scss new file mode 100644 index 0000000..2fe505b --- /dev/null +++ b/java-frontend/src/styles/tailwind.scss @@ -0,0 +1,31 @@ +// 引入 Tailwind 基础样式、组件样式、工具样式 +@tailwind base; +@tailwind components; +@tailwind utilities; + +// 全局样式重置(配合 Tailwind,避免默认样式冲突) +@layer base { + body { + @apply font-sans bg-neutral-100 text-neutral-900; // 后台默认背景、字体 + } + h1 { + @apply text-2xl font-bold; + } + h2 { + @apply text-xl font-semibold; + } + // ... 其他全局标签样式 +} + +// 自定义工具类(可选,比如自定义滚动条) +@layer utilities { + .scrollbar-hide { + scrollbar-width: none; + &::-webkit-scrollbar { + display: none; + } + } + .content-auto { + content-visibility: auto; + } +} diff --git a/java-frontend/src/styles/theme.scss b/java-frontend/src/styles/theme.scss new file mode 100644 index 0000000..63f91a8 --- /dev/null +++ b/java-frontend/src/styles/theme.scss @@ -0,0 +1,56 @@ +// 乐绘世界创想活动乐园 — 主题定制 +// 风格:活泼、艺术、少儿绘本创作 +// 主色调:Creative Indigo(#6366F1)— 激发创造力与想象力 + +:root { + // 主色调 - 创意靛蓝 + --ant-color-primary: #6366f1; + --ant-color-primary-hover: #818cf8; + --ant-color-primary-active: #4f46e5; + --ant-color-primary-bg: #eef2ff; + --ant-color-primary-bg-hover: #e0e7ff; + + // 功能色 — 柔和温暖 + --ant-color-success: #10b981; + --ant-color-success-hover: #34d399; + --ant-color-success-active: #059669; + --ant-color-success-bg: #ecfdf5; + --ant-color-success-bg-hover: #d1fae5; + + --ant-color-info: #6366f1; + --ant-color-info-bg: #eef2ff; + + --ant-color-link: #6366f1; + --ant-color-link-hover: #818cf8; + --ant-color-link-active: #4f46e5; + + --ant-color-error: #f43f5e; + --ant-color-warning: #f59e0b; + + // 文字 + --ant-color-text: #1e1b4b; + --ant-color-text-secondary: #6b7280; + --ant-color-text-tertiary: #9ca3af; + + // 边框 — 紫调柔和 + --ant-color-border: #e5e1f5; + --ant-color-border-secondary: #f0ecf9; + + // 背景 — 暖紫 + --ant-color-bg-container: #ffffff; + --ant-color-bg-layout: #f8f7fc; + + // 侧边栏 + --sidebar-bg: #fefcfb; + --sidebar-menu-item-hover: rgba(99, 102, 241, 0.06); + --sidebar-menu-item-selected-bg: rgba(99, 102, 241, 0.10); + --sidebar-menu-text: #374151; + --sidebar-menu-text-selected: #6366f1; + + // 阴影 + --shadow-card: 0 1px 3px rgba(99, 102, 241, 0.06), + 0 4px 12px rgba(99, 102, 241, 0.04); + --shadow-card-hover: 0 4px 16px rgba(99, 102, 241, 0.10), + 0 8px 32px rgba(99, 102, 241, 0.06); + --shadow-sidebar: 2px 0 16px rgba(99, 102, 241, 0.06); +} diff --git a/java-frontend/src/types/api.ts b/java-frontend/src/types/api.ts new file mode 100644 index 0000000..b5c97bb --- /dev/null +++ b/java-frontend/src/types/api.ts @@ -0,0 +1,17 @@ +export interface ApiResponse { + code: number; + message: string; + data: T; +} + +export interface PaginationParams { + page: number; + pageSize: number; +} + +export interface PaginationResponse { + list: T[]; + total: number; + page: number; + pageSize: number; +} diff --git a/java-frontend/src/types/auth.ts b/java-frontend/src/types/auth.ts new file mode 100644 index 0000000..9197022 --- /dev/null +++ b/java-frontend/src/types/auth.ts @@ -0,0 +1,23 @@ +export interface User { + id: number; + username: string; + nickname: string; + email: string; + avatar?: string; + tenantId?: number; + tenantCode?: string; + roles: string[]; + permissions: string[]; +} + +export interface LoginForm { + username: string; + password: string; + tenantCode?: string; + captcha?: string; +} + +export interface LoginResponse { + token: string; + user: User; +} diff --git a/java-frontend/src/types/router.ts b/java-frontend/src/types/router.ts new file mode 100644 index 0000000..2633463 --- /dev/null +++ b/java-frontend/src/types/router.ts @@ -0,0 +1,11 @@ +import "vue-router"; + +declare module "vue-router" { + interface RouteMeta { + title?: string; + requiresAuth?: boolean; + roles?: string[]; // 需要的角色列表 + permissions?: string[]; // 需要的权限列表 + hideSidebar?: boolean; // 是否隐藏侧边栏(全屏模式) + } +} diff --git a/java-frontend/src/utils/auth.ts b/java-frontend/src/utils/auth.ts new file mode 100644 index 0000000..fa0c999 --- /dev/null +++ b/java-frontend/src/utils/auth.ts @@ -0,0 +1,150 @@ +const TOKEN_KEY = "token"; + +/** + * 从当前 URL 路径中提取租户编码 + */ +function getTenantCodeFromUrl(): string | null { + let path = window.location.pathname; + + // 去掉 base 路径前缀(如 /web-test/ 或 /web/) + const base = import.meta.env.BASE_URL || "/"; + if (base !== "/" && path.startsWith(base)) { + path = path.slice(base.length - 1); // 保留开头的 / + } + + const match = path.match(/^\/([^/]+)/); + return match ? match[1] : null; +} + +/** + * 设置 Cookie + */ +function setCookie( + name: string, + value: string, + options: { path?: string; expires?: number; maxAge?: number } = {} +): void { + const { path = "/", expires, maxAge } = options; + let cookieString = `${name}=${encodeURIComponent(value)}; path=${path}`; + + if (expires) { + const date = new Date(); + date.setTime(date.getTime() + expires * 1000); + cookieString += `; expires=${date.toUTCString()}`; + } + + if (maxAge) { + cookieString += `; max-age=${maxAge}`; + } + + document.cookie = cookieString; +} + +/** + * 获取 Cookie + */ +function getCookie(name: string): string | null { + const nameEQ = name + "="; + const ca = document.cookie.split(";"); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === " ") c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) { + return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + } + return null; +} + +/** + * 删除 Cookie(通过设置过期时间) + */ +function removeCookie(name: string, path: string = "/"): void { + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}`; +} + +/** + * 获取所有可能的 token cookie(因为不同租户可能有不同的 path) + */ +function getAllTokenCookies(): string | null { + // 直接尝试从当前路径获取 cookie + // 注意:由于 cookie 的 path 限制,我们只能读取当前路径或其父路径下的 cookie + // 如果 cookie 的 path 是 /tenant_code,那么只有在 /tenant_code/* 路径下才能读取 + return getCookie(TOKEN_KEY); +} + +export const getToken = (): string | null => { + return getAllTokenCookies(); +}; + +export const setToken = (token: string, tenantCode?: string): void => { + // 始终将 token 存储在根路径下,确保所有页面都能访问 + const base = import.meta.env.BASE_URL || "/"; + const basePath = base.endsWith("/") ? base.slice(0, -1) : base; + const path = basePath || "/"; + + // 设置 cookie,过期时间设置为 7 天 + const expires = 7 * 24 * 60 * 60; // 7 天(秒) + setCookie(TOKEN_KEY, token, { path, expires }); +}; + +export const removeToken = (_tenantCode?: string): void => { + // 删除根路径下的 cookie + const base = import.meta.env.BASE_URL || "/"; + const basePath = base.endsWith("/") ? base.slice(0, -1) : base; + removeCookie(TOKEN_KEY, basePath || "/"); +}; + +/** + * 获取租户编码(从 URL 路径中获取) + * 注意:这些函数不再使用 localStorage,只从 URL 或用户信息中获取 + */ +export const getTenantCode = (): string | null => { + return getTenantCodeFromUrl(); +}; + +/** + * 设置租户编码(已废弃,不再存储到 localStorage) + * 保留此函数以保持兼容性,但不执行任何操作 + */ +export const setTenantCode = (_tenantCode: string): void => { + // 不再存储到 localStorage,租户编码从 URL 或用户信息中获取 +}; + +/** + * 移除租户编码(已废弃,不再从 localStorage 删除) + * 保留此函数以保持兼容性,但不执行任何操作 + */ +export const removeTenantCode = (): void => { + // 不再从 localStorage 删除,租户编码从 URL 或用户信息中获取 +}; + +/** + * 获取租户ID(已废弃,不再从 localStorage 获取) + * 保留此函数以保持兼容性,返回 null + */ +export const getTenantId = (): string | null => { + // 不再从 localStorage 获取,租户ID 从用户信息中获取 + return null; +}; + +/** + * 设置租户ID(已废弃,不再存储到 localStorage) + * 保留此函数以保持兼容性,但不执行任何操作 + */ +export const setTenantId = (_tenantId: number | string): void => { + // 不再存储到 localStorage,租户ID 从用户信息中获取 +}; + +/** + * 移除租户ID(已废弃,不再从 localStorage 删除) + * 保留此函数以保持兼容性,但不执行任何操作 + */ +export const removeTenantId = (): void => { + // 不再从 localStorage 删除,租户ID 从用户信息中获取 +}; + +export const clearAuth = (): void => { + // 只清除 token,租户信息不再存储在 localStorage + removeToken(); +}; diff --git a/java-frontend/src/utils/avatar.ts b/java-frontend/src/utils/avatar.ts new file mode 100644 index 0000000..ce0b811 --- /dev/null +++ b/java-frontend/src/utils/avatar.ts @@ -0,0 +1,86 @@ +/** + * 头像工具函数 + * 使用 DiceBear API 自动生成默认头像 + */ + +// DiceBear 头像风格 +export type AvatarStyle = + | 'initials' // 首字母风格 + | 'avataaars' // 卡通人物风格 + | 'bottts' // 机器人风格 + | 'pixel-art' // 像素风格 + | 'identicon' // GitHub风格的几何图案 + | 'thumbs' // 拇指风格 + | 'shapes' // 几何形状风格 + +export interface AvatarOptions { + style?: AvatarStyle + backgroundColor?: string + textColor?: string + size?: number +} + +const defaultOptions: AvatarOptions = { + style: 'initials', + backgroundColor: '01412b', // 深绿色主题(与侧边栏一致) + textColor: 'ffffff', + size: 64, +} + +/** + * 生成 DiceBear 头像 URL + * @param seed - 用于生成头像的种子值(通常是用户名或ID) + * @param options - 头像配置选项 + * @returns 头像 URL + */ +export function generateAvatarUrl(seed: string, options?: AvatarOptions): string { + const opts = { ...defaultOptions, ...options } + const params = new URLSearchParams() + + params.set('seed', seed) + + if (opts.backgroundColor) { + params.set('backgroundColor', opts.backgroundColor) + } + + if (opts.style === 'initials' && opts.textColor) { + params.set('textColor', opts.textColor) + } + + if (opts.size) { + params.set('size', opts.size.toString()) + } + + return `https://api.dicebear.com/7.x/${opts.style}/svg?${params.toString()}` +} + +/** + * 获取用户头像 URL + * 如果用户有自定义头像则返回自定义头像,否则自动生成默认头像 + * @param user - 用户对象 + * @param options - 头像配置选项 + * @returns 头像 URL + */ +export function getUserAvatar( + user: { avatar?: string | null; username?: string; nickname?: string; id?: number } | null | undefined, + options?: AvatarOptions +): string { + // 如果用户有自定义头像,直接返回 + if (user?.avatar) { + return user.avatar + } + + // 否则根据用户名或昵称生成默认头像 + const seed = user?.username || user?.nickname || user?.id?.toString() || 'user' + return generateAvatarUrl(seed, options) +} + +/** + * 生成随机颜色的头像 + * @param seed - 用于生成头像的种子值 + * @param style - 头像风格 + * @returns 头像 URL + */ +export function generateRandomAvatar(seed: string, style: AvatarStyle = 'initials'): string { + return `https://api.dicebear.com/7.x/${style}/svg?seed=${encodeURIComponent(seed)}` +} diff --git a/java-frontend/src/utils/menu.ts b/java-frontend/src/utils/menu.ts new file mode 100644 index 0000000..3f0aaef --- /dev/null +++ b/java-frontend/src/utils/menu.ts @@ -0,0 +1,343 @@ +import { h } from "vue" +import type { RouteRecordRaw } from "vue-router" +import type { MenuProps } from "ant-design-vue" +import type { Menu } from "@/api/menus" +import * as Icons from "@ant-design/icons-vue" + +/** + * 组件路径映射 + * 将数据库中的组件路径映射到实际的导入函数 + * 注意:Vite 的动态 import() 不支持路径别名,所以需要在这里预先定义所有组件 + */ +// 空布局组件,用于父级菜单 +const EmptyLayout = () => import("@/layouts/EmptyLayout.vue") + +const componentMap: Record Promise> = { + // 工作台模块 + "workbench/Index": () => import("@/views/workbench/Index.vue"), + "workbench/ai-3d/Index": () => import("@/views/workbench/ai-3d/Index.vue"), + // 学校管理模块 + "school/schools/Index": () => import("@/views/school/schools/Index.vue"), + "school/departments/Index": () => + import("@/views/school/departments/Index.vue"), + "school/grades/Index": () => import("@/views/school/grades/Index.vue"), + "school/classes/Index": () => import("@/views/school/classes/Index.vue"), + "school/teachers/Index": () => import("@/views/school/teachers/Index.vue"), + "school/students/Index": () => import("@/views/school/students/Index.vue"), + // 活动管理模块 + "contests/Index": () => import("@/views/contests/Index.vue"), + "contests/Activities": () => import("@/views/contests/Activities.vue"), + "contests/Create": () => import("@/views/contests/Create.vue"), + "contests/Detail": () => import("@/views/contests/Detail.vue"), + "contests/registrations/Index": () => + import("@/views/contests/registrations/Index.vue"), + "contests/works/Index": () => import("@/views/contests/works/Index.vue"), + "contests/works/WorksDetail": () => + import("@/views/contests/works/WorksDetail.vue"), + "contests/reviews/Index": () => import("@/views/contests/reviews/Index.vue"), + "contests/reviews/Tasks": () => import("@/views/contests/reviews/Tasks.vue"), + "contests/reviews/Progress": () => + import("@/views/contests/reviews/Progress.vue"), + "contests/reviews/ProgressDetail": () => + import("@/views/contests/reviews/ProgressDetail.vue"), + "contests/judges/Index": () => import("@/views/contests/judges/Index.vue"), + "contests/results/Index": () => import("@/views/contests/results/Index.vue"), + "contests/notices/Index": () => import("@/views/contests/notices/Index.vue"), + "contests/ReviewRules": () => import("@/views/contests/Index.vue"), // 评审规则临时使用活动列表 + // 内容管理模块 + "content/WorkReview": () => import("@/views/content/WorkReview.vue"), + "content/WorkManagement": () => import("@/views/content/WorkManagement.vue"), + "content/TagManagement": () => import("@/views/content/TagManagement.vue"), + // 作业管理模块 + "homework/Index": () => import("@/views/homework/Index.vue"), + "homework/Submissions": () => import("@/views/homework/Submissions.vue"), + "homework/ReviewRules": () => import("@/views/homework/ReviewRules.vue"), + // 学生端作业模块 + "homework/StudentList": () => import("@/views/homework/StudentList.vue"), + "homework/StudentDetail": () => import("@/views/homework/StudentDetail.vue"), + // 我的评审模块(评委) + "activities/Guidance": () => import("@/views/activities/Guidance.vue"), + "activities/Review": () => import("@/views/activities/Review.vue"), + "activities/ReviewDetail": () => import("@/views/activities/ReviewDetail.vue"), + "activities/Comments": () => import("@/views/activities/Comments.vue"), + "activities/PresetComments": () => import("@/views/activities/PresetComments.vue"), + // 系统管理模块 + "system/users/Index": () => import("@/views/system/users/Index.vue"), + "system/roles/Index": () => import("@/views/system/roles/Index.vue"), + "system/permissions/Index": () => + import("@/views/system/permissions/Index.vue"), + "system/menus/Index": () => import("@/views/system/menus/Index.vue"), + "system/tenants/Index": () => import("@/views/system/tenants/Index.vue"), + "system/dict/Index": () => import("@/views/system/dict/Index.vue"), + "system/config/Index": () => import("@/views/system/config/Index.vue"), + "system/logs/Index": () => import("@/views/system/logs/Index.vue"), + "system/public-users/Index": () => + import("@/views/system/public-users/Index.vue"), +} + +/** + * 获取图标组件 + */ +export function getIconComponent(iconName: string | null | undefined) { + if (!iconName) return null + const IconComponent = (Icons as any)[iconName] + if (IconComponent) { + return () => h(IconComponent) + } + return null +} + +/** + * 从菜单路径生成路由名称(与 convertMenusToRoutes 中的逻辑一致) + * 如果路径相同,使用菜单ID来区分,避免路由名称冲突 + */ +function getRouteNameFromPath( + path: string | null | undefined, + menuId: number, + isChild: boolean = false +): string { + if (path) { + const baseName = path + .split("/") + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join("") + // 如果是子菜单,添加后缀以避免与父菜单路由名称冲突 + return isChild ? `${baseName}${menuId}` : baseName + } + return `Menu${menuId}` +} + +/** + * 将数据库菜单转换为 Ant Design Vue Menu 的 items 格式 + * key 使用路由名称而不是路径 + * 当父菜单只有一个子菜单时,直接显示子菜单,不显示父级折叠结构 + */ +export function convertMenusToMenuItems( + menus: Menu[], + isChild: boolean = false +): MenuProps["items"] { + const result: any[] = [] + + menus.forEach((menu) => { + // 如果只有一个子菜单,直接提升子菜单到当前层级 + if (menu.children && menu.children.length === 1) { + const onlyChild = menu.children[0] + const childRouteName = getRouteNameFromPath(onlyChild.path, onlyChild.id, true) + + const item: any = { + key: childRouteName, + label: onlyChild.name, + title: onlyChild.name, + } + + // 优先使用父菜单的图标,如果没有则使用子菜单的图标 + const iconName = menu.icon || onlyChild.icon + if (iconName) { + const IconComponent = getIconComponent(iconName) + if (IconComponent) { + item.icon = IconComponent + } + } + + // 如果这个唯一的子菜单还有子菜单,继续递归处理 + if (onlyChild.children && onlyChild.children.length > 0) { + item.children = convertMenusToMenuItems(onlyChild.children, true) + } + + result.push(item) + return + } + + // 正常处理:使用路由名称作为 key + const routeName = getRouteNameFromPath(menu.path, menu.id, isChild) + + const item: any = { + key: routeName, + label: menu.name, + title: menu.name, + } + + // 添加图标 + if (menu.icon) { + const IconComponent = getIconComponent(menu.icon) + if (IconComponent) { + item.icon = IconComponent + } + } + + // 如果有多个子菜单,递归处理 + if (menu.children && menu.children.length > 1) { + item.children = convertMenusToMenuItems(menu.children, true) + } + + result.push(item) + }) + + return result +} + +/** + * 将数据库菜单转换为 Vue Router 的路由配置 + * 注意:这些路由会被添加到 /:tenantCode 父路由下,所以不需要再添加 tenantCode 前缀 + */ +/** + * 移除路径中与父路径重合的部分 + */ +function removeParentPathFromRoutePath( + routePath: string, + parentPath: string +): string { + if (!parentPath) { + return routePath + } + + // 标准化路径:移除开头的斜杠 + const normalizedRoutePath = routePath.startsWith("/") + ? routePath.slice(1) + : routePath + const normalizedParentPath = parentPath.startsWith("/") + ? parentPath.slice(1) + : parentPath + + // 如果子路径以父路径开头,移除父路径部分 + if (normalizedRoutePath.startsWith(normalizedParentPath + "/")) { + return normalizedRoutePath.slice(normalizedParentPath.length + 1) + } else if (normalizedRoutePath === normalizedParentPath) { + // 如果路径完全相同,返回空字符串(表示当前路由) + return "" + } + + return normalizedRoutePath +} + +export function convertMenusToRoutes( + menus: Menu[], + parentPath: string = "", + isChild: boolean = false +): RouteRecordRaw[] { + const routes: RouteRecordRaw[] = [] + + menus.forEach((menu) => { + // 构建路由路径 + // 注意:这些路由会被添加到 /:tenantCode 父路由下,所以路径应该是相对路径 + let routePath = menu.path + ? menu.path.startsWith("/") + ? menu.path.slice(1) // 移除开头的斜杠,因为这是相对路径 + : menu.path + : `menu-${menu.id}` + + // 如果有父路径,移除与父路径重合的部分 + if (parentPath) { + routePath = removeParentPathFromRoutePath(routePath, parentPath) + } + + // 构建路由名称(与 convertMenusToMenuItems 中的逻辑一致) + const routeName = getRouteNameFromPath(menu.path, menu.id, isChild) + + // 确定组件加载器 + let componentLoader: (() => Promise) | undefined + if (menu.component) { + // 从组件映射中获取导入函数 + // 如果组件路径以 @/ 开头,说明是完整路径,需要去掉 @/views/ 前缀和 .vue 后缀来匹配 + let componentKey = menu.component + if (componentKey.startsWith("@/views/")) { + componentKey = componentKey.replace("@/views/", "").replace(".vue", "") + } else if (componentKey.endsWith(".vue")) { + componentKey = componentKey.replace(".vue", "") + } + + // 从映射中获取组件导入函数 + const mappedLoader = componentMap[componentKey] + if (mappedLoader) { + componentLoader = mappedLoader + } else { + const componentPath = menu.component + console.warn( + `组件路径 "${componentPath}" (key: "${componentKey}") 未在 componentMap 中定义,请添加到 menu.ts 的 componentMap 中` + ) + // 如果找不到映射,尝试直接导入(可能会失败,但至少不会阻塞) + componentLoader = () => + import( + /* @vite-ignore */ componentPath.startsWith("@/") + ? componentPath + : `@/views/${componentPath}.vue` + ) + } + } else if (menu.children && menu.children.length > 0) { + // 如果没有 component 但有子菜单,使用空布局组件来渲染子路由 + componentLoader = EmptyLayout + } + + // 如果有子菜单,先处理子菜单 + let childrenRoutes: RouteRecordRaw[] | undefined + let redirectToDefaultChild: string | undefined + + if (menu.children && menu.children.length > 0) { + childrenRoutes = convertMenusToRoutes( + menu.children, + menu.path + ? menu.path.startsWith("/") + ? menu.path.slice(1) + : menu.path + : parentPath, + true // 标记为子菜单 + ) + + // 如果父菜单没有 component,但子菜单中有路径为空字符串的路由(默认子路由) + // 应该让父路由重定向到该子路由 + if (!menu.component && childrenRoutes.length > 0) { + const defaultChild = childrenRoutes.find((child) => child.path === "") + if (defaultChild && defaultChild.name) { + // 重定向到默认子路由(使用路由名称) + redirectToDefaultChild = defaultChild.name as string + } + } + } + + // 如果既没有组件也没有子路由,跳过这个菜单(无法渲染) + if (!componentLoader && (!childrenRoutes || childrenRoutes.length === 0)) { + return + } + + const route: RouteRecordRaw = { + path: routePath, + name: routeName, + meta: { + title: menu.name, + requiresAuth: true, + // 如果菜单有权限要求,添加到路由meta中 + ...(menu.permission && { permissions: [menu.permission] }), + }, + ...(componentLoader && { component: componentLoader }), + // 如果有重定向,添加重定向 + ...(redirectToDefaultChild && { + redirect: { name: redirectToDefaultChild }, + }), + // 如果有子菜单,添加子路由 + ...(childrenRoutes && + childrenRoutes.length > 0 && { + children: childrenRoutes, + }), + } as RouteRecordRaw + + routes.push(route) + }) + + return routes +} + +/** + * 扁平化菜单树,用于查找特定路径的菜单 + */ +export function flattenMenus(menus: Menu[]): Menu[] { + const result: Menu[] = [] + + menus.forEach((menu) => { + result.push(menu) + if (menu.children && menu.children.length > 0) { + result.push(...flattenMenus(menu.children)) + } + }) + + return result +} diff --git a/java-frontend/src/utils/request.ts b/java-frontend/src/utils/request.ts new file mode 100644 index 0000000..1311ea6 --- /dev/null +++ b/java-frontend/src/utils/request.ts @@ -0,0 +1,152 @@ +import axios, { + InternalAxiosRequestConfig, + type AxiosInstance, + type AxiosResponse, +} from "axios" +import { message } from "ant-design-vue" +import { getToken, removeToken, getTenantCode } from "./auth" +import { useAuthStore } from "@/stores/auth" +import router from "@/router" + +// 扩展 AxiosInstance 类型,添加 postForm 和 putForm 方法 +interface ExtendedAxiosInstance extends AxiosInstance { + postForm(url: string, data?: any, config?: InternalAxiosRequestConfig): Promise + putForm(url: string, data?: any, config?: InternalAxiosRequestConfig): Promise +} + +const service: ExtendedAxiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || "/api", + timeout: 30000, + headers: { + "Content-Type": "application/json", + }, +}) + +// 添加表单提交的便捷方法 +service.postForm = function (url: string, data?: any, config?: InternalAxiosRequestConfig) { + const formData = new URLSearchParams() + if (data) { + Object.entries(data).forEach(([key, value]) => { + if (value != null) { + formData.append(key, String(value)) + } + }) + } + return this.post(url, formData.toString(), { + ...config, + headers: { + ...config?.headers, + "Content-Type": "application/x-www-form-urlencoded", + }, + }) +} + +service.putForm = function (url: string, data?: any, config?: InternalAxiosRequestConfig) { + const formData = new URLSearchParams() + if (data) { + Object.entries(data).forEach(([key, value]) => { + if (value != null) { + formData.append(key, String(value)) + } + }) + } + return this.put(url, formData.toString(), { + ...config, + headers: { + ...config?.headers, + "Content-Type": "application/x-www-form-urlencoded", + }, + }) +} + +// Request interceptor +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const token = getToken() + if (token && config.headers) { + config.headers.Authorization = `Bearer ${token}` + } + + // 添加租户信息到请求头 + // 租户编码从 URL 获取,租户ID 从用户信息获取 + const tenantCode = getTenantCode() + const authStore = useAuthStore() + const tenantId = authStore.user?.tenantId + + if (config.headers) { + if (tenantCode) { + config.headers["X-Tenant-Code"] = tenantCode + } + if (tenantId) { + config.headers["X-Tenant-Id"] = String(tenantId) + } + } + + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// Response interceptor +service.interceptors.response.use( + (response: AxiosResponse) => { + const res = response.data + + // 如果响应已经是统一格式 { code, message, data } + if (res && typeof res === "object" && "code" in res) { + if (res.code !== 200 && res.code !== 0) { + message.error(res.message || "请求失败") + + if (res.code === 401) { + removeToken() + // 从 URL 获取租户编码,跳转到对应的登录页 + const path = window.location.pathname + const match = path.match(/^\/([^/]+)/) + const tenantCode = match ? match[1] : null + if (tenantCode && tenantCode !== "login") { + router.push(`/${tenantCode}/login`) + } else { + router.push("/login") + } + } + + return Promise.reject(new Error(res.message || "请求失败")) + } + + return res.data !== undefined ? res.data : res + } + + // 如果响应是直接的数据,直接返回 + return res + }, + (error) => { + const errorMessage = + error.response?.data?.message || error.message || "网络错误" + + // 403 权限错误显示更友好的提示 + if (error.response?.status === 403) { + message.error(errorMessage || "您没有权限执行此操作") + } else { + message.error(errorMessage) + } + + if (error.response?.status === 401) { + removeToken() + // 从 URL 获取租户编码,跳转到对应的登录页 + const path = window.location.pathname + const match = path.match(/^\/([^/]+)/) + const tenantCode = match ? match[1] : null + if (tenantCode && tenantCode !== "login") { + router.push(`/${tenantCode}/login`) + } else { + router.push("/login") + } + } + + return Promise.reject(error) + } +) + +export default service diff --git a/java-frontend/src/views/activities/Comments.vue b/java-frontend/src/views/activities/Comments.vue new file mode 100644 index 0000000..2e76fab --- /dev/null +++ b/java-frontend/src/views/activities/Comments.vue @@ -0,0 +1,310 @@ + + + + + diff --git a/java-frontend/src/views/activities/Guidance.vue b/java-frontend/src/views/activities/Guidance.vue new file mode 100644 index 0000000..a337f4d --- /dev/null +++ b/java-frontend/src/views/activities/Guidance.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/java-frontend/src/views/activities/PresetComments.vue b/java-frontend/src/views/activities/PresetComments.vue new file mode 100644 index 0000000..31a5c61 --- /dev/null +++ b/java-frontend/src/views/activities/PresetComments.vue @@ -0,0 +1,479 @@ + + + + + diff --git a/java-frontend/src/views/activities/Review.vue b/java-frontend/src/views/activities/Review.vue new file mode 100644 index 0000000..a049c2b --- /dev/null +++ b/java-frontend/src/views/activities/Review.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/java-frontend/src/views/activities/ReviewDetail.vue b/java-frontend/src/views/activities/ReviewDetail.vue new file mode 100644 index 0000000..05c39bd --- /dev/null +++ b/java-frontend/src/views/activities/ReviewDetail.vue @@ -0,0 +1,408 @@ + + + + + diff --git a/java-frontend/src/views/activities/components/ReviewWorkModal.vue b/java-frontend/src/views/activities/components/ReviewWorkModal.vue new file mode 100644 index 0000000..5ecefb4 --- /dev/null +++ b/java-frontend/src/views/activities/components/ReviewWorkModal.vue @@ -0,0 +1,982 @@ + + + + + diff --git a/java-frontend/src/views/auth/Login.vue b/java-frontend/src/views/auth/Login.vue new file mode 100644 index 0000000..3b84880 --- /dev/null +++ b/java-frontend/src/views/auth/Login.vue @@ -0,0 +1,567 @@ + + + + + diff --git a/java-frontend/src/views/content/TagManagement.vue b/java-frontend/src/views/content/TagManagement.vue new file mode 100644 index 0000000..e6607cd --- /dev/null +++ b/java-frontend/src/views/content/TagManagement.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/java-frontend/src/views/content/WorkManagement.vue b/java-frontend/src/views/content/WorkManagement.vue new file mode 100644 index 0000000..b789a48 --- /dev/null +++ b/java-frontend/src/views/content/WorkManagement.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/java-frontend/src/views/content/WorkReview.vue b/java-frontend/src/views/content/WorkReview.vue new file mode 100644 index 0000000..0b6a479 --- /dev/null +++ b/java-frontend/src/views/content/WorkReview.vue @@ -0,0 +1,239 @@ + + + + + diff --git a/java-frontend/src/views/contests/Activities.vue b/java-frontend/src/views/contests/Activities.vue new file mode 100644 index 0000000..42defd8 --- /dev/null +++ b/java-frontend/src/views/contests/Activities.vue @@ -0,0 +1,1046 @@ + + + + + diff --git a/java-frontend/src/views/contests/Create.vue b/java-frontend/src/views/contests/Create.vue new file mode 100644 index 0000000..f765089 --- /dev/null +++ b/java-frontend/src/views/contests/Create.vue @@ -0,0 +1,936 @@ + + + + + diff --git a/java-frontend/src/views/contests/Detail.vue b/java-frontend/src/views/contests/Detail.vue new file mode 100644 index 0000000..3d5fef6 --- /dev/null +++ b/java-frontend/src/views/contests/Detail.vue @@ -0,0 +1,1245 @@ + + + + + diff --git a/java-frontend/src/views/contests/Guidance.vue b/java-frontend/src/views/contests/Guidance.vue new file mode 100644 index 0000000..edf00ee --- /dev/null +++ b/java-frontend/src/views/contests/Guidance.vue @@ -0,0 +1,792 @@ + + + + + diff --git a/java-frontend/src/views/contests/Index.vue b/java-frontend/src/views/contests/Index.vue new file mode 100644 index 0000000..a6e5c51 --- /dev/null +++ b/java-frontend/src/views/contests/Index.vue @@ -0,0 +1,733 @@ + + + + + diff --git a/java-frontend/src/views/contests/RegisterIndividual.vue b/java-frontend/src/views/contests/RegisterIndividual.vue new file mode 100644 index 0000000..e60600e --- /dev/null +++ b/java-frontend/src/views/contests/RegisterIndividual.vue @@ -0,0 +1,589 @@ + + + + + diff --git a/java-frontend/src/views/contests/RegisterTeam.vue b/java-frontend/src/views/contests/RegisterTeam.vue new file mode 100644 index 0000000..d7bc495 --- /dev/null +++ b/java-frontend/src/views/contests/RegisterTeam.vue @@ -0,0 +1,888 @@ + + + + + diff --git a/java-frontend/src/views/contests/SuperDetail.vue b/java-frontend/src/views/contests/SuperDetail.vue new file mode 100644 index 0000000..205060a --- /dev/null +++ b/java-frontend/src/views/contests/SuperDetail.vue @@ -0,0 +1,500 @@ + + + + + diff --git a/java-frontend/src/views/contests/components/AddJudgeDrawer.vue b/java-frontend/src/views/contests/components/AddJudgeDrawer.vue new file mode 100644 index 0000000..c1cfe34 --- /dev/null +++ b/java-frontend/src/views/contests/components/AddJudgeDrawer.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/java-frontend/src/views/contests/components/AddParticipantDrawer.vue b/java-frontend/src/views/contests/components/AddParticipantDrawer.vue new file mode 100644 index 0000000..4083a09 --- /dev/null +++ b/java-frontend/src/views/contests/components/AddParticipantDrawer.vue @@ -0,0 +1,341 @@ + + + + + + diff --git a/java-frontend/src/views/contests/components/AddTeacherDrawer.vue b/java-frontend/src/views/contests/components/AddTeacherDrawer.vue new file mode 100644 index 0000000..669932c --- /dev/null +++ b/java-frontend/src/views/contests/components/AddTeacherDrawer.vue @@ -0,0 +1,316 @@ + + + + + + diff --git a/java-frontend/src/views/contests/components/SubmitWorkDrawer.vue b/java-frontend/src/views/contests/components/SubmitWorkDrawer.vue new file mode 100644 index 0000000..43f10f8 --- /dev/null +++ b/java-frontend/src/views/contests/components/SubmitWorkDrawer.vue @@ -0,0 +1,879 @@ + + + + + diff --git a/java-frontend/src/views/contests/components/ViewWorkDrawer.vue b/java-frontend/src/views/contests/components/ViewWorkDrawer.vue new file mode 100644 index 0000000..9fff403 --- /dev/null +++ b/java-frontend/src/views/contests/components/ViewWorkDrawer.vue @@ -0,0 +1,681 @@ + + + + + diff --git a/java-frontend/src/views/contests/components/WorkDetailModal.vue b/java-frontend/src/views/contests/components/WorkDetailModal.vue new file mode 100644 index 0000000..37db6ca --- /dev/null +++ b/java-frontend/src/views/contests/components/WorkDetailModal.vue @@ -0,0 +1,535 @@ + + + + + diff --git a/java-frontend/src/views/contests/judges/Index.vue b/java-frontend/src/views/contests/judges/Index.vue new file mode 100644 index 0000000..29a946c --- /dev/null +++ b/java-frontend/src/views/contests/judges/Index.vue @@ -0,0 +1,627 @@ + + + + + diff --git a/java-frontend/src/views/contests/notices/Index.vue b/java-frontend/src/views/contests/notices/Index.vue new file mode 100644 index 0000000..361d64e --- /dev/null +++ b/java-frontend/src/views/contests/notices/Index.vue @@ -0,0 +1,700 @@ + + + + + diff --git a/java-frontend/src/views/contests/registrations/Index.vue b/java-frontend/src/views/contests/registrations/Index.vue new file mode 100644 index 0000000..1f4404c --- /dev/null +++ b/java-frontend/src/views/contests/registrations/Index.vue @@ -0,0 +1,602 @@ + + + + + diff --git a/java-frontend/src/views/contests/registrations/Records.vue b/java-frontend/src/views/contests/registrations/Records.vue new file mode 100644 index 0000000..3f3501d --- /dev/null +++ b/java-frontend/src/views/contests/registrations/Records.vue @@ -0,0 +1,1077 @@ + + + + + diff --git a/java-frontend/src/views/contests/results/Detail.vue b/java-frontend/src/views/contests/results/Detail.vue new file mode 100644 index 0000000..8122fd5 --- /dev/null +++ b/java-frontend/src/views/contests/results/Detail.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/java-frontend/src/views/contests/results/Index.vue b/java-frontend/src/views/contests/results/Index.vue new file mode 100644 index 0000000..3b76ab7 --- /dev/null +++ b/java-frontend/src/views/contests/results/Index.vue @@ -0,0 +1,460 @@ + + + + + diff --git a/java-frontend/src/views/contests/reviews/Index.vue b/java-frontend/src/views/contests/reviews/Index.vue new file mode 100644 index 0000000..07e955a --- /dev/null +++ b/java-frontend/src/views/contests/reviews/Index.vue @@ -0,0 +1,685 @@ + + + + + diff --git a/java-frontend/src/views/contests/reviews/Progress.vue b/java-frontend/src/views/contests/reviews/Progress.vue new file mode 100644 index 0000000..0b258d7 --- /dev/null +++ b/java-frontend/src/views/contests/reviews/Progress.vue @@ -0,0 +1,591 @@ + + + + + diff --git a/java-frontend/src/views/contests/reviews/ProgressDetail.vue b/java-frontend/src/views/contests/reviews/ProgressDetail.vue new file mode 100644 index 0000000..316da86 --- /dev/null +++ b/java-frontend/src/views/contests/reviews/ProgressDetail.vue @@ -0,0 +1,727 @@ + + + + + diff --git a/java-frontend/src/views/contests/reviews/Tasks.vue b/java-frontend/src/views/contests/reviews/Tasks.vue new file mode 100644 index 0000000..795ae85 --- /dev/null +++ b/java-frontend/src/views/contests/reviews/Tasks.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/java-frontend/src/views/contests/works/Index.vue b/java-frontend/src/views/contests/works/Index.vue new file mode 100644 index 0000000..7e41015 --- /dev/null +++ b/java-frontend/src/views/contests/works/Index.vue @@ -0,0 +1,468 @@ + + + + + diff --git a/java-frontend/src/views/contests/works/WorksDetail.vue b/java-frontend/src/views/contests/works/WorksDetail.vue new file mode 100644 index 0000000..d0caee8 --- /dev/null +++ b/java-frontend/src/views/contests/works/WorksDetail.vue @@ -0,0 +1,780 @@ + + + + + diff --git a/java-frontend/src/views/error/403.vue b/java-frontend/src/views/error/403.vue new file mode 100644 index 0000000..822a3a7 --- /dev/null +++ b/java-frontend/src/views/error/403.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/java-frontend/src/views/error/404.vue b/java-frontend/src/views/error/404.vue new file mode 100644 index 0000000..9df4528 --- /dev/null +++ b/java-frontend/src/views/error/404.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/java-frontend/src/views/homework/Index.vue b/java-frontend/src/views/homework/Index.vue new file mode 100644 index 0000000..f2d426c --- /dev/null +++ b/java-frontend/src/views/homework/Index.vue @@ -0,0 +1,601 @@ + + + + + diff --git a/java-frontend/src/views/homework/ReviewRules.vue b/java-frontend/src/views/homework/ReviewRules.vue new file mode 100644 index 0000000..75f147f --- /dev/null +++ b/java-frontend/src/views/homework/ReviewRules.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/java-frontend/src/views/homework/StudentDetail.vue b/java-frontend/src/views/homework/StudentDetail.vue new file mode 100644 index 0000000..b7313e4 --- /dev/null +++ b/java-frontend/src/views/homework/StudentDetail.vue @@ -0,0 +1,507 @@ + + + + + diff --git a/java-frontend/src/views/homework/StudentList.vue b/java-frontend/src/views/homework/StudentList.vue new file mode 100644 index 0000000..b1ada1b --- /dev/null +++ b/java-frontend/src/views/homework/StudentList.vue @@ -0,0 +1,495 @@ + + + + + diff --git a/java-frontend/src/views/homework/Submissions.vue b/java-frontend/src/views/homework/Submissions.vue new file mode 100644 index 0000000..bd5dc7c --- /dev/null +++ b/java-frontend/src/views/homework/Submissions.vue @@ -0,0 +1,537 @@ + + + + + diff --git a/java-frontend/src/views/model/ModelViewer.vue b/java-frontend/src/views/model/ModelViewer.vue new file mode 100644 index 0000000..15495bd --- /dev/null +++ b/java-frontend/src/views/model/ModelViewer.vue @@ -0,0 +1,2360 @@ + + + + + diff --git a/java-frontend/src/views/public/Activities.vue b/java-frontend/src/views/public/Activities.vue new file mode 100644 index 0000000..263e250 --- /dev/null +++ b/java-frontend/src/views/public/Activities.vue @@ -0,0 +1,310 @@ + + + + + diff --git a/java-frontend/src/views/public/ActivityDetail.vue b/java-frontend/src/views/public/ActivityDetail.vue new file mode 100644 index 0000000..8c30646 --- /dev/null +++ b/java-frontend/src/views/public/ActivityDetail.vue @@ -0,0 +1,651 @@ + + + + + diff --git a/java-frontend/src/views/public/Gallery.vue b/java-frontend/src/views/public/Gallery.vue new file mode 100644 index 0000000..9279978 --- /dev/null +++ b/java-frontend/src/views/public/Gallery.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/java-frontend/src/views/public/Login.vue b/java-frontend/src/views/public/Login.vue new file mode 100644 index 0000000..209e7c8 --- /dev/null +++ b/java-frontend/src/views/public/Login.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/java-frontend/src/views/public/components/WorkSelector.vue b/java-frontend/src/views/public/components/WorkSelector.vue new file mode 100644 index 0000000..b1f864f --- /dev/null +++ b/java-frontend/src/views/public/components/WorkSelector.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/java-frontend/src/views/public/create/Generating.vue b/java-frontend/src/views/public/create/Generating.vue new file mode 100644 index 0000000..0ccc4a8 --- /dev/null +++ b/java-frontend/src/views/public/create/Generating.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/java-frontend/src/views/public/create/Index.vue b/java-frontend/src/views/public/create/Index.vue new file mode 100644 index 0000000..d3616f0 --- /dev/null +++ b/java-frontend/src/views/public/create/Index.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/java-frontend/src/views/public/mine/Children.vue b/java-frontend/src/views/public/mine/Children.vue new file mode 100644 index 0000000..d4ec833 --- /dev/null +++ b/java-frontend/src/views/public/mine/Children.vue @@ -0,0 +1,407 @@ + + + + + diff --git a/java-frontend/src/views/public/mine/Index.vue b/java-frontend/src/views/public/mine/Index.vue new file mode 100644 index 0000000..59312e2 --- /dev/null +++ b/java-frontend/src/views/public/mine/Index.vue @@ -0,0 +1,331 @@ + + + + + diff --git a/java-frontend/src/views/public/mine/Registrations.vue b/java-frontend/src/views/public/mine/Registrations.vue new file mode 100644 index 0000000..b5d5bcd --- /dev/null +++ b/java-frontend/src/views/public/mine/Registrations.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/java-frontend/src/views/public/mine/Works.vue b/java-frontend/src/views/public/mine/Works.vue new file mode 100644 index 0000000..4ad580d --- /dev/null +++ b/java-frontend/src/views/public/mine/Works.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/java-frontend/src/views/public/works/Detail.vue b/java-frontend/src/views/public/works/Detail.vue new file mode 100644 index 0000000..ec95da4 --- /dev/null +++ b/java-frontend/src/views/public/works/Detail.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/java-frontend/src/views/public/works/Index.vue b/java-frontend/src/views/public/works/Index.vue new file mode 100644 index 0000000..13e76be --- /dev/null +++ b/java-frontend/src/views/public/works/Index.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/java-frontend/src/views/public/works/Publish.vue b/java-frontend/src/views/public/works/Publish.vue new file mode 100644 index 0000000..3d8d0ca --- /dev/null +++ b/java-frontend/src/views/public/works/Publish.vue @@ -0,0 +1,239 @@ + + + + + diff --git a/java-frontend/src/views/school/classes/Index.vue b/java-frontend/src/views/school/classes/Index.vue new file mode 100644 index 0000000..4b60bfc --- /dev/null +++ b/java-frontend/src/views/school/classes/Index.vue @@ -0,0 +1,369 @@ + + + + + diff --git a/java-frontend/src/views/school/departments/Index.vue b/java-frontend/src/views/school/departments/Index.vue new file mode 100644 index 0000000..ca197c3 --- /dev/null +++ b/java-frontend/src/views/school/departments/Index.vue @@ -0,0 +1,351 @@ + + + + + diff --git a/java-frontend/src/views/school/grades/Index.vue b/java-frontend/src/views/school/grades/Index.vue new file mode 100644 index 0000000..9addc11 --- /dev/null +++ b/java-frontend/src/views/school/grades/Index.vue @@ -0,0 +1,280 @@ + + + + + diff --git a/java-frontend/src/views/school/schools/Index.vue b/java-frontend/src/views/school/schools/Index.vue new file mode 100644 index 0000000..3b09967 --- /dev/null +++ b/java-frontend/src/views/school/schools/Index.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/java-frontend/src/views/school/students/Index.vue b/java-frontend/src/views/school/students/Index.vue new file mode 100644 index 0000000..f1367f5 --- /dev/null +++ b/java-frontend/src/views/school/students/Index.vue @@ -0,0 +1,552 @@ + + + + + diff --git a/java-frontend/src/views/school/teachers/Index.vue b/java-frontend/src/views/school/teachers/Index.vue new file mode 100644 index 0000000..2c9d44e --- /dev/null +++ b/java-frontend/src/views/school/teachers/Index.vue @@ -0,0 +1,498 @@ + + + + + diff --git a/java-frontend/src/views/system/config/Index.vue b/java-frontend/src/views/system/config/Index.vue new file mode 100644 index 0000000..7a8ad8f --- /dev/null +++ b/java-frontend/src/views/system/config/Index.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/java-frontend/src/views/system/dict/Index.vue b/java-frontend/src/views/system/dict/Index.vue new file mode 100644 index 0000000..ba00e83 --- /dev/null +++ b/java-frontend/src/views/system/dict/Index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/java-frontend/src/views/system/logs/Index.vue b/java-frontend/src/views/system/logs/Index.vue new file mode 100644 index 0000000..7df7db0 --- /dev/null +++ b/java-frontend/src/views/system/logs/Index.vue @@ -0,0 +1,504 @@ + + + + + diff --git a/java-frontend/src/views/system/menus/Index.vue b/java-frontend/src/views/system/menus/Index.vue new file mode 100644 index 0000000..4c814e5 --- /dev/null +++ b/java-frontend/src/views/system/menus/Index.vue @@ -0,0 +1,298 @@ + + + diff --git a/java-frontend/src/views/system/permissions/Index.vue b/java-frontend/src/views/system/permissions/Index.vue new file mode 100644 index 0000000..78d580e --- /dev/null +++ b/java-frontend/src/views/system/permissions/Index.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/java-frontend/src/views/system/public-users/Index.vue b/java-frontend/src/views/system/public-users/Index.vue new file mode 100644 index 0000000..4492292 --- /dev/null +++ b/java-frontend/src/views/system/public-users/Index.vue @@ -0,0 +1,354 @@ + + + + + diff --git a/java-frontend/src/views/system/roles/Index.vue b/java-frontend/src/views/system/roles/Index.vue new file mode 100644 index 0000000..9d40dce --- /dev/null +++ b/java-frontend/src/views/system/roles/Index.vue @@ -0,0 +1,374 @@ + + + + + diff --git a/java-frontend/src/views/system/tenants/Index.vue b/java-frontend/src/views/system/tenants/Index.vue new file mode 100644 index 0000000..a8ef765 --- /dev/null +++ b/java-frontend/src/views/system/tenants/Index.vue @@ -0,0 +1,605 @@ + + + + + diff --git a/java-frontend/src/views/system/users/Index.vue b/java-frontend/src/views/system/users/Index.vue new file mode 100644 index 0000000..d5fc3c7 --- /dev/null +++ b/java-frontend/src/views/system/users/Index.vue @@ -0,0 +1,769 @@ + + + + + diff --git a/java-frontend/src/views/workbench/Index.vue b/java-frontend/src/views/workbench/Index.vue new file mode 100644 index 0000000..9bda8fc --- /dev/null +++ b/java-frontend/src/views/workbench/Index.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/java-frontend/src/views/workbench/ai-3d/Generate.vue b/java-frontend/src/views/workbench/ai-3d/Generate.vue new file mode 100644 index 0000000..3535a6b --- /dev/null +++ b/java-frontend/src/views/workbench/ai-3d/Generate.vue @@ -0,0 +1,1081 @@ + + + + + diff --git a/java-frontend/src/views/workbench/ai-3d/History.vue b/java-frontend/src/views/workbench/ai-3d/History.vue new file mode 100644 index 0000000..d10c30a --- /dev/null +++ b/java-frontend/src/views/workbench/ai-3d/History.vue @@ -0,0 +1,961 @@ + + + + + diff --git a/java-frontend/src/views/workbench/ai-3d/Index.vue b/java-frontend/src/views/workbench/ai-3d/Index.vue new file mode 100644 index 0000000..bdf2aa2 --- /dev/null +++ b/java-frontend/src/views/workbench/ai-3d/Index.vue @@ -0,0 +1,2822 @@ + + + + + + + + diff --git a/java-frontend/src/vite-env.d.ts b/java-frontend/src/vite-env.d.ts new file mode 100644 index 0000000..f4d7cda --- /dev/null +++ b/java-frontend/src/vite-env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} + diff --git a/java-frontend/tailwind.config.js b/java-frontend/tailwind.config.js new file mode 100644 index 0000000..49be99b --- /dev/null +++ b/java-frontend/tailwind.config.js @@ -0,0 +1,55 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], + theme: { + extend: { + // 自定义颜色(品牌色、功能色) + // colors: { + // primary: { + // // 主色调(比如后台常用的蓝色) + // 50: "#f0f9ff", + // 100: "#e0f2fe", + // 500: "#0ea5e9", // 常用主色 + // 600: "#0284c7", // 加深主色 + // }, + // secondary: { + // // 辅助色(比如橙色) + // 500: "#f97316", + // }, + // neutral: { + // // 中性色(背景、文本) + // 100: "#f8fafc", + // 200: "#e2e8f0", + // 700: "#334155", + // 900: "#0f172a", + // }, + // }, + // // 自定义间距(按 8px 基准,后台常用) + // spacing: { + // 1: "4px", + // 2: "8px", + // 3: "12px", + // 4: "16px", + // 5: "20px", + // 6: "24px", + // // ... 更多间距 + // }, + // // 自定义字体(后台常用无衬线字体) + // fontFamily: { + // sans: ["Inter", "system-ui", "sans-serif"], + // }, + // // 自定义边框圆角 + // borderRadius: { + // sm: "4px", + // md: "6px", + // lg: "8px", + // }, + }, + }, + plugins: [ + // require('tailwindcss-animate'), // 额外动画插件 + ], + corePlugins: { + preflight: false, + }, +}; diff --git a/java-frontend/tsconfig.json b/java-frontend/tsconfig.json new file mode 100644 index 0000000..0e2bfc7 --- /dev/null +++ b/java-frontend/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + /* Path alias */ + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} + diff --git a/java-frontend/tsconfig.node.json b/java-frontend/tsconfig.node.json new file mode 100644 index 0000000..e428d50 --- /dev/null +++ b/java-frontend/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} + diff --git a/java-frontend/tsconfig.node.tsbuildinfo b/java-frontend/tsconfig.node.tsbuildinfo new file mode 100644 index 0000000..99239ea --- /dev/null +++ b/java-frontend/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.scripthost.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.float16.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","../node_modules/.pnpm/buffer@5.7.1/node_modules/buffer/index.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","../node_modules/.pnpm/@types+estree@1.0.8/node_modules/@types/estree/index.d.ts","../node_modules/.pnpm/rollup@4.53.3/node_modules/rollup/dist/rollup.d.ts","../node_modules/.pnpm/rollup@4.53.3/node_modules/rollup/dist/parseast.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/types/hmrpayload.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/types/customevent.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/types/hot.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","../node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","../node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/previous-map.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/input.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/css-syntax-error.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/declaration.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/root.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/warning.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/lazy-result.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/no-work-result.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/processor.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/result.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/document.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/rule.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/node.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/comment.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/container.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/at-rule.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/list.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/postcss.d.ts","../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/postcss.d.mts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/dist/node/runtime.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/types/importglob.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/types/metadata.d.ts","../node_modules/.pnpm/vite@5.4.21_@types+node@20.19.25_sass@1.94.1_terser@5.44.1/node_modules/vite/dist/node/index.d.ts","../node_modules/.pnpm/@babel+types@7.28.5/node_modules/@babel/types/lib/index.d.ts","../node_modules/.pnpm/@vue+shared@3.5.24/node_modules/@vue/shared/dist/shared.d.ts","../node_modules/.pnpm/@babel+parser@7.28.5/node_modules/@babel/parser/typings/babel-parser.d.ts","../node_modules/.pnpm/@vue+compiler-core@3.5.24/node_modules/@vue/compiler-core/dist/compiler-core.d.ts","../node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.d.mts","../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/typescript.d.ts","../node_modules/.pnpm/@vue+compiler-sfc@3.5.24/node_modules/@vue/compiler-sfc/dist/compiler-sfc.d.ts","../node_modules/.pnpm/vue@3.5.24_typescript@5.9.3/node_modules/vue/compiler-sfc/index.d.mts","../node_modules/.pnpm/@vitejs+plugin-vue@5.2.4_vi_e70672b9796244f49d066a536954e836/node_modules/@vitejs/plugin-vue/dist/index.d.mts","./vite.config.ts","./node_modules/@types/connect/index.d.ts","./node_modules/@types/body-parser/index.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/event-emitter/index.d.ts","./node_modules/@types/send/index.d.ts","./node_modules/@types/qs/index.d.ts","./node_modules/@types/range-parser/index.d.ts","./node_modules/@types/express-serve-static-core/index.d.ts","./node_modules/@types/http-errors/index.d.ts","./node_modules/@types/serve-static/index.d.ts","./node_modules/@types/express/index.d.ts","../node_modules/.pnpm/@types+body-parser@1.19.6/node_modules/@types/body-parser/index.d.ts","../node_modules/.pnpm/@types+send@1.2.1/node_modules/@types/send/index.d.ts","../node_modules/.pnpm/@types+qs@6.14.0/node_modules/@types/qs/index.d.ts","../node_modules/.pnpm/@types+range-parser@1.2.7/node_modules/@types/range-parser/index.d.ts","../node_modules/.pnpm/@types+express-serve-static-core@4.19.7/node_modules/@types/express-serve-static-core/index.d.ts","../node_modules/.pnpm/@types+http-errors@2.0.5/node_modules/@types/http-errors/index.d.ts","../node_modules/.pnpm/@types+mime@1.3.5/node_modules/@types/mime/index.d.ts","../node_modules/.pnpm/@types+send@0.17.6/node_modules/@types/send/index.d.ts","../node_modules/.pnpm/@types+serve-static@1.15.10/node_modules/@types/serve-static/index.d.ts","../node_modules/.pnpm/@types+express@4.17.25/node_modules/@types/express/index.d.ts","../node_modules/.pnpm/@types+multer@2.0.0/node_modules/@types/multer/index.d.ts","../node_modules/.pnpm/@types+adm-zip@0.5.7/node_modules/@types/adm-zip/util.d.ts","../node_modules/.pnpm/@types+adm-zip@0.5.7/node_modules/@types/adm-zip/index.d.ts","../node_modules/.pnpm/@types+babel__generator@7.27.0/node_modules/@types/babel__generator/index.d.ts","../node_modules/.pnpm/@types+babel__template@7.4.4/node_modules/@types/babel__template/index.d.ts","../node_modules/.pnpm/@types+babel__traverse@7.28.0/node_modules/@types/babel__traverse/index.d.ts","../node_modules/.pnpm/@types+babel__core@7.20.5/node_modules/@types/babel__core/index.d.ts","../node_modules/.pnpm/@types+bcrypt@5.0.2/node_modules/@types/bcrypt/index.d.ts","../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts","../node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/use-at-your-own-risk.d.ts","../node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index.d.ts","../node_modules/.pnpm/@types+eslint-scope@3.7.7/node_modules/@types/eslint-scope/index.d.ts","../node_modules/.pnpm/@types+graceful-fs@4.1.9/node_modules/@types/graceful-fs/index.d.ts","../node_modules/.pnpm/@types+istanbul-lib-coverage@2.0.6/node_modules/@types/istanbul-lib-coverage/index.d.ts","../node_modules/.pnpm/@types+istanbul-lib-report@3.0.3/node_modules/@types/istanbul-lib-report/index.d.ts","../node_modules/.pnpm/@types+istanbul-reports@3.0.4/node_modules/@types/istanbul-reports/index.d.ts","../node_modules/.pnpm/@jest+expect-utils@29.7.0/node_modules/@jest/expect-utils/build/index.d.ts","../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/index.d.ts","../node_modules/.pnpm/@sinclair+typebox@0.27.8/node_modules/@sinclair/typebox/typebox.d.ts","../node_modules/.pnpm/@jest+schemas@29.6.3/node_modules/@jest/schemas/build/index.d.ts","../node_modules/.pnpm/pretty-format@29.7.0/node_modules/pretty-format/build/index.d.ts","../node_modules/.pnpm/jest-diff@29.7.0/node_modules/jest-diff/build/index.d.ts","../node_modules/.pnpm/jest-matcher-utils@29.7.0/node_modules/jest-matcher-utils/build/index.d.ts","../node_modules/.pnpm/expect@29.7.0/node_modules/expect/build/index.d.ts","../node_modules/.pnpm/@types+jest@29.5.14/node_modules/@types/jest/index.d.ts","../node_modules/.pnpm/@types+jsonwebtoken@9.0.5/node_modules/@types/jsonwebtoken/index.d.ts","../node_modules/.pnpm/@types+ms@2.1.0/node_modules/@types/ms/index.d.ts","../node_modules/.pnpm/@types+passport@1.0.17/node_modules/@types/passport/index.d.ts","../node_modules/.pnpm/@types+jsonwebtoken@9.0.10/node_modules/@types/jsonwebtoken/index.d.ts","../node_modules/.pnpm/@types+passport-strategy@0.2.38/node_modules/@types/passport-strategy/index.d.ts","../node_modules/.pnpm/@types+passport-jwt@4.0.1/node_modules/@types/passport-jwt/index.d.ts","../node_modules/.pnpm/@types+passport-local@1.0.38/node_modules/@types/passport-local/index.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/inc.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/classes/semver.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/parse.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/valid.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/clean.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/diff.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/major.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/minor.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/patch.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/prerelease.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/compare.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/rcompare.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/compare-loose.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/compare-build.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/sort.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/rsort.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/gt.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/lt.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/eq.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/neq.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/gte.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/lte.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/cmp.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/coerce.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/classes/comparator.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/classes/range.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/functions/satisfies.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/max-satisfying.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/min-satisfying.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/to-comparators.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/min-version.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/valid.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/outside.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/gtr.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/ltr.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/intersects.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/simplify.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/ranges/subset.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/internals/identifiers.d.ts","../node_modules/.pnpm/@types+semver@7.7.1/node_modules/@types/semver/index.d.ts","../node_modules/.pnpm/@types+stack-utils@2.0.3/node_modules/@types/stack-utils/index.d.ts","../node_modules/.pnpm/@types+uuid@10.0.0/node_modules/@types/uuid/index.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isboolean.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isemail.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isfqdn.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isiban.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isiso31661alpha2.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isiso4217.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isiso6391.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/istaxid.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/lib/isurl.d.ts","../node_modules/.pnpm/@types+validator@13.15.10/node_modules/@types/validator/index.d.ts","../node_modules/.pnpm/@types+yargs-parser@21.0.3/node_modules/@types/yargs-parser/index.d.ts","../node_modules/.pnpm/@types+yargs@17.0.35/node_modules/@types/yargs/index.d.ts"],"fileIdsList":[[54,117,125,129,131,132,134,135,136,148,173,216],[54,117,125,129,131,132,134,135,136,148,173],[54,117,125,129,132,134,135,136,148],[54,117,125,128,129,131,132,134,135,136,148,173,220,221,222],[54,117,125,129,132,134,135,136,148,217,223,225],[54,114,115,117,125,129,132,134,135,136,148],[54,116,117,125,129,132,134,135,136,148],[117,125,129,132,134,135,136,148],[54,117,125,129,132,134,135,136,148,156],[54,117,118,123,125,128,129,132,134,135,136,138,148,153,165],[54,117,118,119,125,128,129,132,134,135,136,148],[54,117,120,125,129,132,134,135,136,148,166],[54,117,121,122,125,129,132,134,135,136,139,148],[54,117,122,125,129,132,134,135,136,148,153,162],[54,117,123,125,128,129,132,134,135,136,138,148],[54,116,117,124,125,129,132,134,135,136,148],[54,117,125,126,129,132,134,135,136,148],[54,117,125,127,128,129,132,134,135,136,148],[54,116,117,125,128,129,132,134,135,136,148],[54,117,125,128,129,130,132,134,135,136,148,153,165],[54,117,125,128,129,130,132,134,135,136,148,153,156],[54,104,117,125,128,129,131,132,134,135,136,138,148,153,165],[54,117,125,128,129,131,132,134,135,136,138,148,153,162,165],[54,117,125,129,131,132,133,134,135,136,148,153,162,165],[52,53,54,55,56,57,58,59,60,61,62,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[54,117,125,128,129,132,134,135,136,148],[54,117,125,129,132,134,136,148],[54,117,125,129,132,134,135,136,137,148,165],[54,117,125,128,129,132,134,135,136,138,148,153],[54,117,125,129,132,134,135,136,139,148],[54,117,125,129,132,134,135,136,140,148],[54,117,125,128,129,132,134,135,136,143,148],[54,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[54,117,125,129,132,134,135,136,145,148],[54,117,125,129,132,134,135,136,146,148],[54,117,122,125,129,132,134,135,136,138,148,156],[54,117,125,128,129,132,134,135,136,148,149],[54,117,125,129,132,134,135,136,148,150,166,169],[54,117,125,128,129,132,134,135,136,148,153,155,156],[54,117,125,129,132,134,135,136,148,154,156],[54,117,125,129,132,134,135,136,148,156,166],[54,117,125,129,132,134,135,136,148,157],[54,114,117,125,129,132,134,135,136,148,153,159],[54,117,125,129,132,134,135,136,148,153,158],[54,117,125,128,129,132,134,135,136,148,160,161],[54,117,125,129,132,134,135,136,148,160,161],[54,117,122,125,129,132,134,135,136,138,148,153,162],[54,117,125,129,132,134,135,136,148,163],[54,117,125,129,132,134,135,136,138,148,164],[54,117,125,129,131,132,134,135,136,146,148,165],[54,117,125,129,132,134,135,136,148,166,167],[54,117,122,125,129,132,134,135,136,148,167],[54,117,125,129,132,134,135,136,148,153,168],[54,117,125,129,132,134,135,136,137,148,169],[54,117,125,129,132,134,135,136,148,170],[54,117,120,125,129,132,134,135,136,148],[54,117,122,125,129,132,134,135,136,148],[54,117,125,129,132,134,135,136,148,166],[54,104,117,125,129,132,134,135,136,148],[54,117,125,129,132,134,135,136,148,165],[54,117,125,129,132,134,135,136,148,171],[54,117,125,129,132,134,135,136,143,148],[54,117,125,129,132,134,135,136,148,161],[54,104,117,125,128,129,130,132,134,135,136,143,148,153,156,165,168,169,171],[54,117,125,129,132,134,135,136,148,153,172],[54,117,125,129,132,134,135,136,148,153,173],[54,117,125,129,131,132,134,135,136,148,173,224],[54,70,73,76,77,117,125,129,132,134,135,136,148,165],[54,73,117,125,129,132,134,135,136,148,153,165],[54,73,77,117,125,129,132,134,135,136,148,165],[54,117,125,129,132,134,135,136,148,153],[54,67,117,125,129,132,134,135,136,148],[54,71,117,125,129,132,134,135,136,148],[54,69,70,73,117,125,129,132,134,135,136,148,165],[54,117,125,129,132,134,135,136,138,148,162],[54,117,125,129,132,134,135,136,148,173],[54,67,117,125,129,132,134,135,136,148,173],[54,69,73,117,125,129,132,134,135,136,138,148,165],[54,64,65,66,68,72,117,125,128,129,132,134,135,136,148,153,165],[54,73,81,89,117,125,129,132,134,135,136,148],[54,65,71,117,125,129,132,134,135,136,148],[54,73,98,99,117,125,129,132,134,135,136,148],[54,65,68,73,117,125,129,132,134,135,136,148,156,165,173],[54,73,117,125,129,132,134,135,136,148],[54,69,73,117,125,129,132,134,135,136,148,165],[54,64,117,125,129,132,134,135,136,148],[54,67,68,69,71,72,73,74,75,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,117,125,129,132,134,135,136,148],[54,73,91,94,117,125,129,132,134,135,136,148],[54,73,81,82,83,117,125,129,132,134,135,136,148],[54,71,73,82,84,117,125,129,132,134,135,136,148],[54,72,117,125,129,132,134,135,136,148],[54,65,67,73,117,125,129,132,134,135,136,148],[54,73,77,82,84,117,125,129,132,134,135,136,148],[54,77,117,125,129,132,134,135,136,148],[54,71,73,76,117,125,129,132,134,135,136,148,165],[54,65,69,73,81,117,125,129,132,134,135,136,148],[54,73,91,117,125,129,132,134,135,136,148],[54,84,117,125,129,132,134,135,136,148],[54,67,73,98,117,125,129,132,134,135,136,148,156,171,173],[54,117,125,129,132,134,135,136,140,148,205,214],[54,117,125,129,132,134,135,136,148,206],[54,117,125,129,132,134,135,136,148,255],[54,117,125,129,132,134,135,136,148,173,238],[54,117,125,129,132,134,135,136,148,206,208,240,241,242],[54,117,125,129,132,134,135,136,148,206,208],[54,117,125,129,131,132,134,135,136,148,216],[54,117,125,129,132,134,135,136,148,174,175,247],[54,117,125,129,132,134,135,136,148,174,175,245,246],[54,117,125,129,132,134,135,136,148,247],[54,117,125,129,132,134,135,136,148,217,221,223,225,231,235],[54,117,125,129,132,134,135,136,148,250],[54,117,125,129,132,134,135,136,148,251],[54,117,125,129,132,134,135,136,148,257,260],[54,117,122,125,129,132,134,135,136,148,173,263],[54,117,122,125,129,132,134,135,136,148,173],[54,117,125,129,132,134,135,136,148,153,236],[54,117,125,129,132,134,135,136,148,265,266],[54,117,125,129,132,134,135,136,148,236,264,266],[54,117,125,129,132,134,135,136,148,226,236,264],[54,117,125,129,131,132,134,135,136,148,236],[54,117,125,129,132,134,135,136,148,270,308],[54,117,125,129,132,134,135,136,148,270,293,308],[54,117,125,129,132,134,135,136,148,269,308],[54,117,125,129,132,134,135,136,148,308],[54,117,125,129,132,134,135,136,148,270],[54,117,125,129,132,134,135,136,148,270,294,308],[54,117,125,129,132,134,135,136,148,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307],[54,117,125,129,132,134,135,136,148,294,308],[54,117,125,129,132,134,135,136,148,153,173,233],[54,117,125,129,131,132,134,135,136,148,173,224,234],[54,117,125,129,132,134,135,136,148,311,312,313,314,315,316,317,318,319],[54,117,125,129,132,134,135,136,148,321],[54,117,125,129,132,134,135,136,148,205,213],[54,117,125,129,132,134,135,136,148,206,207,208],[54,117,125,129,132,134,135,136,148,201,206,208,209,210,211],[54,117,125,129,132,134,135,136,148,253,259],[54,117,125,129,132,134,135,136,148,257],[54,117,125,129,132,134,135,136,148,254,258],[54,117,125,129,132,134,135,136,148,197],[54,117,125,129,132,134,135,136,148,195,197],[54,117,125,129,132,134,135,136,148,186,194,195,196,198,200],[54,117,125,129,132,134,135,136,148,184],[54,117,125,129,132,134,135,136,148,187,192,197,200],[54,117,125,129,132,134,135,136,148,183,200],[54,117,125,129,132,134,135,136,148,187,188,191,192,193,200],[54,117,125,129,132,134,135,136,148,187,188,189,191,192,200],[54,117,125,129,132,134,135,136,148,184,185,186,187,188,192,193,194,196,197,198,200],[54,117,125,129,132,134,135,136,148,200],[54,117,125,129,132,134,135,136,148,182,184,185,186,187,188,189,191,192,193,194,195,196,197,198,199],[54,117,125,129,132,134,135,136,148,182,200],[54,117,125,129,132,134,135,136,148,187,189,190,192,193,200],[54,117,125,129,132,134,135,136,148,191,200],[54,117,125,129,132,134,135,136,148,192,193,197,200],[54,117,125,129,132,134,135,136,148,185,195],[54,117,125,129,132,134,135,136,148,256],[54,117,125,129,132,134,135,136,148,175,204],[54,117,125,129,132,134,135,136,148,174,175],[54,117,125,128,129,131,132,133,134,135,136,138,148,153,162,165,172,173,175,176,177,178,179,180,181,201,202,203,204],[54,117,125,129,132,134,135,136,148,177,178,179,180],[54,117,125,129,132,134,135,136,148,177,178,179],[54,117,125,129,132,134,135,136,148,177],[54,117,125,129,132,134,135,136,148,178],[54,117,125,129,132,134,135,136,148,175],[54,117,125,129,132,134,135,136,148,212]],"fileInfos":[{"version":"a7297ff837fcdf174a9524925966429eb8e5feecc2cc55cc06574e6b092c1eaa","impliedFormat":1},{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"487b694c3de27ddf4ad107d4007ad304d29effccf9800c8ae23c2093638d906a","impliedFormat":1},{"version":"3a80bc85f38526ca3b08007ee80712e7bb0601df178b23fbf0bf87036fce40ce","impliedFormat":1},{"version":"ccf4552357ce3c159ef75f0f0114e80401702228f1898bdc9402214c9499e8c0","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"2931540c47ee0ff8a62860e61782eb17b155615db61e36986e54645ec67f67c2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"f6faf5f74e4c4cc309a6c6a6c4da02dbb840be5d3e92905a23dcd7b2b0bd1986","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"33e981bf6376e939f99bd7f89abec757c64897d33c005036b9a10d9587d80187","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"b41767d372275c154c7ea6c9d5449d9a741b8ce080f640155cc88ba1763e35b3","impliedFormat":1},{"version":"3bacf516d686d08682751a3bd2519ea3b8041a164bfb4f1d35728993e70a2426","impliedFormat":1},{"version":"7fb266686238369442bd1719bc0d7edd0199da4fb8540354e1ff7f16669b4323","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"54c3e2371e3d016469ad959697fd257e5621e16296fa67082c2575d0bf8eced0","impliedFormat":1},{"version":"beb8233b2c220cfa0feea31fbe9218d89fa02faa81ef744be8dce5acb89bb1fd","impliedFormat":1},{"version":"c183b931b68ad184bc8e8372bf663f3d33304772fb482f29fb91b3c391031f3e","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"48cc3ec153b50985fb95153258a710782b25975b10dd4ac8a4f3920632d10790","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"e1528ca65ac90f6fa0e4a247eb656b4263c470bb22d9033e466463e13395e599","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"866078923a56d026e39243b4392e282c1c63159723996fa89243140e1388a98d","impliedFormat":1},{"version":"dd0109710de4cd93e245121ab86d8c66d20f3ead80074b68e9c3e349c4f53342","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"435b3711465425770ed2ee2f1cf00ce071835265e0851a7dc4600ab4b007550e","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"cf83d90d5faf27b994c2e79af02e32b555dbfe42cd9bd1571445f2168d1f4e2d","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"0e28335ac43f4d94dd2fe6d9e6fa6813570640839addd10d309d7985f33a6308","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"b0b69c61b0f0ec8ca15db4c8c41f6e77f4cacb784d42bca948f42dea33e8757e","affectsGlobalScope":true,"impliedFormat":1},{"version":"f96a48183254c00d24575401f1a761b4ce4927d927407e7862a83e06ce5d6964","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"ac86245c2f31335bfd52cbe7fc760f9fc4f165387875869a478a6d9616a95e72","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"9d96a7ce809392ff2cb99691acf7c62e632fe56897356ba013b689277aca3619","impliedFormat":1},{"version":"42a05d8f239f74587d4926aba8cc54792eed8e8a442c7adc9b38b516642aadfe","impliedFormat":1},{"version":"5d21b58d60383cc6ab9ad3d3e265d7d25af24a2c9b506247e0e50b0a884920be","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"ae6757460f37078884b1571a3de3ebaf724d827d7e1d53626c02b3c2a408ac63","affectsGlobalScope":true,"impliedFormat":1},{"version":"27c0a08e343c6a0ae17bd13ba6d44a9758236dc904cd5e4b43456996cd51f520","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"6f80e51ba310608cd71bcdc09a171d7bbfb3b316048601c9ec215ce16a8dcfbc","impliedFormat":1},{"version":"70ac82add6c6f5c8a4a1db5390e75ead5a0cf5cd50216cf22769870c7026c786","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"af4ab0aa8908fc9a655bb833d3bc28e117c4f0e1038c5a891546158beb25accb","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"2ca2bca6845a7234eff5c3d192727a068fca72ac565f3c819c6b04ccc83dadc0","impliedFormat":1},{"version":"ed4f674fc8c0c993cc7e145069ac44129e03519b910c62be206a0cc777bdc60b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"17d06eb5709839c7ce719f0c38ada6f308fb433f2cd6d8c87b35856e07400950","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"2a00d005e3af99cd1cfa75220e60c61b04bfb6be7ca7453bfe2ef6cca37cc03c","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"14d4bd22d1b05824971b98f7e91b2484c90f1a684805c330476641417c3d9735","impliedFormat":1},{"version":"586eaf66bace2e731cee0ddfbfac326ad74a83c1acfeac4afb2db85ad23226c7","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"d1a14d87cedcf4f0b8173720d6eb29cc02878bf2b6dabf9c9d9cee742f275368","impliedFormat":1},{"version":"e60efae9fe48a2955f66bf4cbf0f082516185b877daf50d9c5e2a009660a7714","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"b37f83e7deea729aa9ce5593f78905afb45b7532fdff63041d374f60059e7852","impliedFormat":1},{"version":"e1cb68f3ef3a8dd7b2a9dfb3de482ed6c0f1586ba0db4e7d73c1d2147b6ffc51","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"6cd8f2410e4cf6d7870f018b38dcf1ac4771f06b363b5d71831d924cda3c488d","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"333caa2bfff7f06017f114de738050dd99a765c7eb16571c6d25a38c0d5365dc","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"459920181700cec8cbdf2a5faca127f3f17fd8dd9d9e577ed3f5f3af5d12a2e4","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"70790a7f0040993ca66ab8a07a059a0f8256e7bb57d968ae945f696cbff4ac7a","impliedFormat":1},{"version":"d1b9a81e99a0050ca7f2d98d7eedc6cda768f0eb9fa90b602e7107433e64c04c","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"b215c4f0096f108020f666ffcc1f072c81e9f2f95464e894a5d5f34c5ea2a8b1","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"dfe54dab1fa4961a6bcfba68c4ca955f8b5bbeb5f2ab3c915aa7adaa2eabc03a","impliedFormat":1},{"version":"1251d53755b03cde02466064260bb88fd83c30006a46395b7d9167340bc59b73","impliedFormat":1},{"version":"47865c5e695a382a916b1eedda1b6523145426e48a2eae4647e96b3b5e52024f","impliedFormat":1},{"version":"4cdf27e29feae6c7826cdd5c91751cc35559125e8304f9e7aed8faef97dcf572","impliedFormat":1},{"version":"331b8f71bfae1df25d564f5ea9ee65a0d847c4a94baa45925b6f38c55c7039bf","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"c2c2a861a338244d7dd700d0c52a78916b4bb75b98fc8ca5e7c501899fc03796","impliedFormat":1},{"version":"f468b74459f1ad4473b36a36d49f2b255f3c6b5d536c81239c2b2971df089eaf","impliedFormat":1},{"version":"adb467429462e3891de5bb4a82a4189b92005d61c7f9367c089baf03997c104e","impliedFormat":1},{"version":"7f108fc2f0dd96e1ce5e6942c555538afc4d836d4e22b60cdbe034caaac7b521","impliedFormat":1},{"version":"2be2227c3810dfd84e46674fd33b8d09a4a28ad9cb633ed536effd411665ea1e","impliedFormat":99},{"version":"e134052a6b1ded61693b4037f615dc72f14e2881e79c1ddbff6c514c8a516b05","impliedFormat":1},{"version":"83eeb5fc6bc433785dec98525eb003a02134024a8630134ecc67404d0075c26e","impliedFormat":1},{"version":"3feec212c0aeb91e5a6e62caaf9f128954590210f8c302910ea377c088f6b61a","impliedFormat":99},{"version":"bbdfaf7d9b20534c5df1e1b937a20f17ca049d603a2afe072983bf7aff2279f5","impliedFormat":99},{"version":"97ab2abbd3d680156129967147b157dfb2eeb90b0ba60238627a59a697c6f59c","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"2dc1bcd6a132924a89f965b60edd5b5333aaa7eb28ba5af44a0a0a328756e9ad","impliedFormat":1},{"version":"d34aa8df2d0b18fb56b1d772ff9b3c7aea7256cf0d692f969be6e1d27b74d660","impliedFormat":1},{"version":"baac9896d29bcc55391d769e408ff400d61273d832dd500f21de766205255acb","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"6823ccc7b5b77bbf898d878dbcad18aa45e0fa96bdd0abd0de98d514845d9ed9","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"168d88e14e0d81fe170e0dadd38ae9d217476c11435ea640ddb9b7382bdb6c1f","impliedFormat":1},{"version":"8e04cf0688e0d921111659c2b55851957017148fa7b977b02727477d155b3c47","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"d34aa8df2d0b18fb56b1d772ff9b3c7aea7256cf0d692f969be6e1d27b74d660","impliedFormat":1},{"version":"baac9896d29bcc55391d769e408ff400d61273d832dd500f21de766205255acb","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"94ee9ee71018d54902c3fe6730090a8a421dcad95fc372d9b69a6d5351194885","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"d3f2d715f57df3f04bf7b16dde01dec10366f64fce44503c92b8f78f614c1769","impliedFormat":1},{"version":"cb90077223cc1365fa21ef0911a1f9b8f2f878943523d97350dc557973ca3823","impliedFormat":1},{"version":"18f1541b81b80d806120a3489af683edfb811deb91aeca19735d9bb2613e6311","impliedFormat":1},{"version":"232f118ae64ab84dcd26ddb60eaed5a6e44302d36249abf05e9e3fc2cbb701a2","impliedFormat":1},{"version":"58564964bef3ffbd810241a8bd1c3a54347dd8adf04e1077ba49051009d3007d","affectsGlobalScope":true,"impliedFormat":1},{"version":"9063bd95bf4fe06fe71b4f1abd4dce7d41b684d9036ff4afae6e079eb252b19e","impliedFormat":1},{"version":"bb0c5cb27578678fe7a8479404efd988ceab67ff93238baf16cef721610658f7","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"160b24efb5a868df9c54f337656b4ef55fcbe0548fe15408e1c0630ec559c559","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"a4a39b5714adfcadd3bbea6698ca2e942606d833bde62ad5fb6ec55f5e438ff8","impliedFormat":1},{"version":"bbc1d029093135d7d9bfa4b38cbf8761db505026cc458b5e9c8b74f4000e5e75","impliedFormat":1},{"version":"1f68ab0e055994eb337b67aa87d2a15e0200951e9664959b3866ee6f6b11a0fe","impliedFormat":1},{"version":"afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","impliedFormat":1},{"version":"035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","impliedFormat":1},{"version":"a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","impliedFormat":1},{"version":"5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","impliedFormat":1},{"version":"cdcc132f207d097d7d3aa75615ab9a2e71d6a478162dde8b67f88ea19f3e54de","impliedFormat":1},{"version":"0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","impliedFormat":1},{"version":"c085e9aa62d1ae1375794c1fb927a445fa105fed891a7e24edbb1c3300f7384a","impliedFormat":1},{"version":"f315e1e65a1f80992f0509e84e4ae2df15ecd9ef73df975f7c98813b71e4c8da","impliedFormat":1},{"version":"5b9586e9b0b6322e5bfbd2c29bd3b8e21ab9d871f82346cb71020e3d84bae73e","impliedFormat":1},{"version":"3e70a7e67c2cb16f8cd49097360c0309fe9d1e3210ff9222e9dac1f8df9d4fb6","impliedFormat":1},{"version":"ab68d2a3e3e8767c3fba8f80de099a1cfc18c0de79e42cb02ae66e22dfe14a66","impliedFormat":1},{"version":"d96cc6598148bf1a98fb2e8dcf01c63a4b3558bdaec6ef35e087fd0562eb40ec","impliedFormat":1},{"version":"f8db4fea512ab759b2223b90ecbbe7dae919c02f8ce95ec03f7fb1cf757cfbeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bf811dcbddc95e2551f704cfd2afc267bf619f8b8f2b7bdbb94df96ec3cbfe3","impliedFormat":1},{"version":"fb893a0dfc3c9fb0f9ca93d0648694dd95f33cbad2c0f2c629f842981dfd4e2e","impliedFormat":1},{"version":"ff155930718467b27e379e4a195e4607ce277f805cad9d2fa5f4fd5dec224df6","affectsGlobalScope":true,"impliedFormat":1},{"version":"95da3c365e3d45709ad6e0b4daa5cdaf05e9076ba3c201e8f8081dd282c02f57","impliedFormat":1},{"version":"03c92769f389dbd9e45232f7eb01c3e0f482b62555aaf2029dcbf380d5cee9e4","impliedFormat":1},{"version":"32d7f70fd3498bc76a46dab8b03af4215f445f490f8e213c80cf06b636a4e413","impliedFormat":1},{"version":"17668c1aab598920796050ee5a00d961ede5e92595f6ac8908a975ed75a537e5","impliedFormat":1},{"version":"ce6a3f09b8db73a7e9701aca91a04b4fabaf77436dd35b24482f9ee816016b17","impliedFormat":1},{"version":"20e086e5b64fdd52396de67761cc0e94693494deadb731264aac122adf08de3f","impliedFormat":1},{"version":"6e78f75403b3ec65efb41c70d392aeda94360f11cedc9fb2c039c9ea23b30962","impliedFormat":1},{"version":"c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","impliedFormat":1},{"version":"8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","impliedFormat":1},{"version":"42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","impliedFormat":1},{"version":"ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","impliedFormat":1},{"version":"83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","impliedFormat":1},{"version":"1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","impliedFormat":1},{"version":"0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","impliedFormat":1},{"version":"cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","impliedFormat":1},{"version":"c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","impliedFormat":1},{"version":"eefd2bbc8edb14c3bd1246794e5c070a80f9b8f3730bd42efb80df3cc50b9039","impliedFormat":1},{"version":"0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","impliedFormat":1},{"version":"7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","impliedFormat":1},{"version":"bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","impliedFormat":1},{"version":"52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","impliedFormat":1},{"version":"770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","impliedFormat":1},{"version":"d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","impliedFormat":1},{"version":"799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","impliedFormat":1},{"version":"2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","impliedFormat":1},{"version":"9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","impliedFormat":1},{"version":"397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","impliedFormat":1},{"version":"a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","impliedFormat":1},{"version":"a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","impliedFormat":1},{"version":"c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","impliedFormat":1},{"version":"4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","impliedFormat":1},{"version":"f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","impliedFormat":1},{"version":"cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","impliedFormat":1},{"version":"b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","impliedFormat":1},{"version":"c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","impliedFormat":1},{"version":"14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","impliedFormat":1},{"version":"a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","impliedFormat":1},{"version":"f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","impliedFormat":1},{"version":"3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","impliedFormat":1},{"version":"662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","impliedFormat":1},{"version":"c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","impliedFormat":1},{"version":"2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","impliedFormat":1},{"version":"34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","impliedFormat":1},{"version":"a56fe175741cc8841835eb72e61fa5a34adcbc249ede0e3494c229f0750f6b85","impliedFormat":1},{"version":"ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","impliedFormat":1},{"version":"f874ea4d0091b0a44362a5f74d26caab2e66dec306c2bf7e8965f5106e784c3b","impliedFormat":1},{"version":"c6cdcd12d577032b84eed1de4d2de2ae343463701a25961b202cff93989439fb","impliedFormat":1},{"version":"3dc633586d48fcd04a4f8acdbf7631b8e4a334632f252d5707e04b299069721e","impliedFormat":1},{"version":"3322858f01c0349ee7968a5ce93a1ca0c154c4692aa8f1721dc5192a9191a168","impliedFormat":1},{"version":"6dde0a77adad4173a49e6de4edd6ef70f5598cbebb5c80d76c111943854636ca","impliedFormat":1},{"version":"09acacae732e3cc67a6415026cfae979ebe900905500147a629837b790a366b3","impliedFormat":1},{"version":"f7b622759e094a3c2e19640e0cb233b21810d2762b3e894ef7f415334125eb22","impliedFormat":1},{"version":"99236ea5c4c583082975823fd19bcce6a44963c5c894e20384bc72e7eccf9b03","impliedFormat":1},{"version":"f6688a02946a3f7490aa9e26d76d1c97a388e42e77388cbab010b69982c86e9e","impliedFormat":1},{"version":"9f642953aba68babd23de41de85d4e97f0c39ef074cb8ab8aa7d55237f62aff6","impliedFormat":1},{"version":"159d95163a0ed369175ae7838fa21a9e9e703de5fdb0f978721293dd403d9f4a","impliedFormat":1},{"version":"bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","impliedFormat":1},{"version":"26a770cec4bd2e7dbba95c6e536390fffe83c6268b78974a93727903b515c4e7","impliedFormat":1}],"root":[215],"options":{"allowSyntheticDefaultImports":true,"composite":true,"module":99,"skipLibCheck":true},"referencedMap":[[217,1],[216,2],[218,3],[219,3],[223,4],[226,5],[224,3],[114,6],[115,6],[116,7],[54,8],[117,9],[118,10],[119,11],[52,3],[120,12],[121,13],[122,14],[123,15],[124,16],[125,17],[126,17],[127,18],[128,19],[129,20],[130,21],[55,3],[53,3],[131,22],[132,23],[133,24],[173,25],[134,26],[135,27],[136,26],[137,28],[138,29],[139,30],[140,31],[141,31],[142,31],[143,32],[144,33],[145,34],[146,35],[147,36],[148,37],[149,37],[150,38],[151,3],[152,3],[153,39],[154,40],[155,39],[156,41],[157,42],[158,43],[159,44],[160,45],[161,46],[162,47],[163,48],[164,49],[165,50],[166,51],[167,52],[168,53],[169,54],[170,55],[56,26],[57,3],[58,56],[59,57],[60,3],[61,58],[62,3],[105,59],[106,60],[107,61],[108,61],[109,62],[110,3],[111,9],[112,63],[113,60],[171,64],[172,65],[221,3],[222,3],[220,66],[225,67],[81,68],[93,69],[79,70],[94,71],[103,72],[70,73],[71,74],[69,75],[102,76],[97,77],[101,78],[73,79],[90,80],[72,81],[100,82],[67,83],[68,77],[74,84],[75,3],[80,85],[78,84],[65,86],[104,87],[95,88],[84,89],[83,84],[85,90],[88,91],[82,92],[86,93],[98,76],[76,94],[77,95],[89,96],[66,71],[92,97],[91,84],[87,98],[96,3],[64,3],[99,99],[215,100],[208,101],[206,3],[253,3],[256,102],[255,3],[239,103],[238,3],[243,104],[240,101],[241,105],[242,101],[244,76],[227,106],[248,107],[247,108],[246,109],[174,3],[231,4],[236,110],[249,76],[232,3],[250,3],[251,111],[252,112],[261,113],[245,3],[265,114],[262,115],[233,3],[263,3],[237,116],[267,117],[268,118],[266,119],[264,120],[229,3],[230,3],[293,121],[294,122],[270,123],[273,124],[291,121],[292,121],[282,121],[281,125],[279,121],[274,121],[287,121],[285,121],[289,121],[269,121],[286,121],[290,121],[275,121],[276,121],[288,121],[271,121],[277,121],[278,121],[280,121],[284,121],[295,126],[283,121],[272,121],[308,127],[307,3],[302,126],[304,128],[303,126],[296,126],[297,126],[299,126],[301,126],[305,128],[306,128],[298,128],[300,128],[234,129],[228,71],[235,130],[309,3],[310,3],[320,131],[311,3],[312,3],[313,3],[314,3],[315,3],[316,3],[317,3],[318,3],[319,3],[321,3],[322,132],[214,133],[209,134],[212,135],[207,3],[63,3],[254,3],[181,3],[260,136],[258,137],[259,138],[210,3],[198,139],[196,140],[197,141],[185,142],[186,140],[193,143],[184,144],[189,145],[199,3],[190,146],[195,147],[201,148],[200,149],[183,150],[191,151],[192,152],[187,153],[194,139],[188,154],[257,155],[176,156],[175,157],[182,3],[1,3],[50,3],[51,3],[9,3],[13,3],[12,3],[3,3],[14,3],[15,3],[16,3],[17,3],[18,3],[19,3],[20,3],[21,3],[4,3],[22,3],[23,3],[5,3],[24,3],[28,3],[25,3],[26,3],[27,3],[29,3],[30,3],[31,3],[6,3],[32,3],[33,3],[34,3],[35,3],[7,3],[39,3],[36,3],[37,3],[38,3],[40,3],[8,3],[41,3],[46,3],[47,3],[42,3],[43,3],[44,3],[45,3],[2,3],[48,3],[49,3],[11,3],[10,3],[211,3],[205,158],[202,159],[180,160],[178,161],[177,3],[179,162],[203,3],[204,163],[213,164]],"latestChangedDtsFile":"./vite.config.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/java-frontend/tsconfig.tsbuildinfo b/java-frontend/tsconfig.tsbuildinfo new file mode 100644 index 0000000..6b48206 --- /dev/null +++ b/java-frontend/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/ai-3d.ts","./src/api/auth.ts","./src/api/classes.ts","./src/api/config.ts","./src/api/contests.ts","./src/api/departments.ts","./src/api/dict.ts","./src/api/grades.ts","./src/api/homework.ts","./src/api/judges-management.ts","./src/api/logs.ts","./src/api/menus.ts","./src/api/permissions.ts","./src/api/roles.ts","./src/api/schools.ts","./src/api/students.ts","./src/api/teachers.ts","./src/api/tenants.ts","./src/api/upload.ts","./src/api/users.ts","./src/composables/uselistrequest.ts","./src/composables/usesimplelistrequest.ts","./src/directives/permission.ts","./src/router/index.ts","./src/stores/auth.ts","./src/types/api.ts","./src/types/auth.ts","./src/types/router.ts","./src/utils/auth.ts","./src/utils/avatar.ts","./src/utils/menu.ts","./src/utils/request.ts","./src/app.vue","./src/components/modelviewer.vue","./src/components/richtexteditor.vue","./src/layouts/basiclayout.vue","./src/layouts/emptylayout.vue","./src/views/activities/comments.vue","./src/views/activities/guidance.vue","./src/views/activities/review.vue","./src/views/activities/reviewdetail.vue","./src/views/activities/components/reviewworkmodal.vue","./src/views/auth/login.vue","./src/views/contests/activities.vue","./src/views/contests/create.vue","./src/views/contests/detail.vue","./src/views/contests/guidance.vue","./src/views/contests/index.vue","./src/views/contests/registerindividual.vue","./src/views/contests/registerteam.vue","./src/views/contests/components/addjudgedrawer.vue","./src/views/contests/components/addparticipantdrawer.vue","./src/views/contests/components/addteacherdrawer.vue","./src/views/contests/components/submitworkdrawer.vue","./src/views/contests/components/viewworkdrawer.vue","./src/views/contests/components/workdetailmodal.vue","./src/views/contests/judges/index.vue","./src/views/contests/notices/index.vue","./src/views/contests/registrations/index.vue","./src/views/contests/registrations/records.vue","./src/views/contests/results/detail.vue","./src/views/contests/results/index.vue","./src/views/contests/reviews/index.vue","./src/views/contests/reviews/progress.vue","./src/views/contests/reviews/progressdetail.vue","./src/views/contests/reviews/tasks.vue","./src/views/contests/works/index.vue","./src/views/contests/works/worksdetail.vue","./src/views/error/403.vue","./src/views/error/404.vue","./src/views/homework/index.vue","./src/views/homework/reviewrules.vue","./src/views/homework/studentdetail.vue","./src/views/homework/studentlist.vue","./src/views/homework/submissions.vue","./src/views/model/modelviewer.vue","./src/views/school/classes/index.vue","./src/views/school/departments/index.vue","./src/views/school/grades/index.vue","./src/views/school/schools/index.vue","./src/views/school/students/index.vue","./src/views/school/teachers/index.vue","./src/views/system/config/index.vue","./src/views/system/dict/index.vue","./src/views/system/logs/index.vue","./src/views/system/menus/index.vue","./src/views/system/permissions/index.vue","./src/views/system/roles/index.vue","./src/views/system/tenants/index.vue","./src/views/system/users/index.vue","./src/views/workbench/index.vue","./src/views/workbench/ai-3d/generate.vue","./src/views/workbench/ai-3d/history.vue","./src/views/workbench/ai-3d/index.vue"],"errors":true,"version":"5.9.3"} \ No newline at end of file diff --git a/java-frontend/vite.config.ts b/java-frontend/vite.config.ts new file mode 100644 index 0000000..3578627 --- /dev/null +++ b/java-frontend/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite" +import vue from "@vitejs/plugin-vue" +import { resolve } from "path" + +// 根据环境设置 base 路径 +const getBase = (mode: string) => { + switch (mode) { + case "test": + return "/web-test/" + case "production": + return "/web/" + default: + return "/" + } +} + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => { + return { + base: getBase(mode), + plugins: [vue()], + resolve: { + alias: { + "@": resolve(__dirname, "src"), + }, + }, + server: { + port: 3000, + proxy: { + "/api": { + target: "http://localhost:3234", + changeOrigin: true, + // rewrite: (path) => path.replace(/^\/api/, ''), + }, + }, + }, + } +}) diff --git a/java-frontend/项目分析文档.md b/java-frontend/项目分析文档.md new file mode 100644 index 0000000..bb407a9 --- /dev/null +++ b/java-frontend/项目分析文档.md @@ -0,0 +1,897 @@ +# Vue 3 前端项目分析报告 + +> 项目路径:`frontend/` +> 分析日期:2026-03-28 +> 项目类型:多租户竞赛/活动管理系统前端 + +--- + +## 一、技术栈概览 + +| 组件 | 技术选型 | 版本 | +|------|----------|------| +| **框架** | Vue 3 | 3.4.21 | +| **构建工具** | Vite | 5.1.6 | +| **语言** | TypeScript | 5.4.3 | +| **状态管理** | Pinia | 2.1.7 | +| **路由** | Vue Router | 4.3.0 | +| **UI 组件库** | Ant Design Vue | 4.1.1 | +| **图标** | @ant-design/icons-vue | 7.0.1 | +| **HTTP 客户端** | Axios | 1.6.7 | +| **表单验证** | Vee Validate + Zod | 4.12.4 / 3.22.4 | +| **富文本编辑器** | WangEditor | 5.1.12 | +| **3D 渲染** | Three.js | 0.182.0 | +| **样式** | Tailwind CSS + SCSS | 3.4.1 | +| **日期处理** | Day.js | 1.11.10 | + +### 完整依赖列表 + +```json +{ + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@vee-validate/zod": "^4.12.4", + "@wangeditor/editor": "^5.1.23", + "@wangeditor-editor-for-vue": "^5.1.12", + "ant-design-vue": "^4.1.1", + "axios": "^1.6.7", + "dayjs": "^1.11.10", + "pinia": "^2.1.7", + "three": "^0.182.0", + "vee-validate": "^4.12.4", + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "autoprefixer": "^10.4.18", + "eslint": "^8.57.0", + "eslint-plugin-vue": "^9.22.0", + "postcss": "^8.4.35", + "sass": "^1.71.1", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.3", + "vite": "^5.1.6", + "vue-tsc": "^3.2.2" + } +} +``` + +--- + +## 二、项目架构 + +### 目录结构 + +``` +frontend/ +├── src/ +│ ├── api/ # API 接口层 +│ │ ├── ai-3d.ts # AI 3D 任务接口 +│ │ ├── auth.ts # 认证接口 +│ │ ├── classes.ts # 班级管理接口 +│ │ ├── config.ts # 系统配置接口 +│ │ ├── contests.ts # 竞赛管理接口(核心) +│ │ ├── departments.ts # 部门管理接口 +│ │ ├── dict.ts # 数据字典接口 +│ │ ├── grades.ts # 年级管理接口 +│ │ ├── homework.ts # 作业管理接口 +│ │ ├── judges-management.ts # 评委管理接口 +│ │ ├── logs.ts # 日志管理接口 +│ │ ├── menus.ts # 菜单管理接口 +│ │ ├── permissions.ts # 权限管理接口 +│ │ ├── preset-comments.ts # 预设评语接口 +│ │ ├── public.ts # 公共接口 +│ │ ├── roles.ts # 角色管理接口 +│ │ ├── schools.ts # 学校管理接口 +│ │ ├── students.ts # 学生管理接口 +│ │ ├── teachers.ts # 教师管理接口 +│ │ ├── tenants.ts # 租户管理接口 +│ │ ├── upload.ts # 文件上传接口 +│ │ └── users.ts # 用户管理接口 +│ │ +│ ├── components/ # 公共组件 +│ │ ├── ModelViewer.vue # 3D 模型预览组件 +│ │ └── RichTextEditor.vue # 富文本编辑器组件 +│ │ +│ ├── composables/ # 组合式函数 +│ │ ├── useListRequest.ts # 列表请求 Hook +│ │ └── useSimpleListRequest.ts # 简单列表请求 Hook +│ │ +│ ├── directives/ # 自定义指令 +│ │ └── permission.ts # 权限指令 +│ │ +│ ├── layouts/ # 布局组件 +│ │ ├── BasicLayout.vue # 管理端基础布局 +│ │ ├── EmptyLayout.vue # 空白布局 +│ │ └── PublicLayout.vue # 公众端布局 +│ │ +│ ├── router/ # 路由配置 +│ │ └── index.ts # 路由主文件(含动态路由) +│ │ +│ ├── stores/ # Pinia 状态管理 +│ │ └── auth.ts # 认证状态 +│ │ +│ ├── types/ # TypeScript 类型定义 +│ │ ├── api.ts # API 通用类型 +│ │ ├── auth.ts # 认证相关类型 +│ │ └── router.ts # 路由扩展类型 +│ │ +│ ├── utils/ # 工具函数 +│ │ ├── auth.ts # 认证工具 +│ │ ├── avatar.ts # 头像工具 +│ │ ├── menu.ts # 菜单工具 +│ │ └── request.ts # Axios 封装 +│ │ +│ └── views/ # 页面组件 +│ ├── activities/ # 活动评审页面 +│ ├── auth/ # 认证页面 +│ ├── content/ # 内容管理页面 +│ ├── contests/ # 竞赛管理页面(核心) +│ ├── error/ # 错误页面 +│ ├── homework/ # 作业管理页面 +│ ├── model/ # 3D 模型页面 +│ ├── public/ # 公众端页面 +│ ├── school/ # 学校管理页面 +│ ├── system/ # 系统管理页面 +│ └── workbench/ # 工作台页面 +│ +├── scripts/ # 构建脚本 +└── package.json +``` + +--- + +## 三、核心功能模块详解 + +### 1. 认证授权模块 (`auth/`) + +**文件结构:** +``` +src/stores/auth.ts # Pinia Store +src/api/auth.ts # API 接口 +src/views/auth/Login.vue # 登录页面 +src/utils/auth.ts # 认证工具 +``` + +**核心功能:** +- JWT Token 认证 +- 用户信息获取 +- 动态菜单加载 +- 权限/角色判断 +- 租户编码管理 + +**核心代码示例:** + +```typescript +// src/stores/auth.ts +export const useAuthStore = defineStore("auth", () => { + const user = ref(null) + const token = ref(getToken() || "") + const menus = ref([]) + + const isAuthenticated = computed(() => !!token.value) + + // 检查是否为超级管理员 + const isSuperAdmin = (): boolean => { + return user.value?.roles?.includes('super_admin') ?? false + } + + // 检查是否有指定权限(超管自动拥有所有权限) + const hasPermission = (permission: string): boolean => { + if (isSuperAdmin()) return true + return user.value?.permissions?.includes(permission) ?? false + } + + const login = async (form: LoginForm) => { + const response = await authApi.login(form) + token.value = response.token + user.value = response.user + await fetchUserMenus() // 获取用户菜单 + return response + } + + // ... +}) +``` + +--- + +### 2. 路由系统 + +**核心特性:** +- 基于动态菜单的路由加载 +- 租户编码路径前缀(`/:tenantCode/`) +- 权限守卫 +- 公众端与管理端分离 + +**路由结构:** + +```typescript +// 基础路由 +const baseRoutes = [ + // 管理端登录 + { path: "/:tenantCode/login", component: Login }, + + // 公众端路由(/p 前缀) + { + path: "/p", + children: [ + { path: "gallery", name: "PublicGallery" }, // 作品广场 + { path: "activities", name: "PublicActivities" }, // 活动大厅 + { path: "mine", name: "PublicMine" }, // 个人中心 + { path: "create", name: "PublicCreate" }, // 绘本创作 + ] + }, + + // 管理端路由(动态加载) + { + path: "/:tenantCode", + name: "Main", + component: BasicLayout, + children: [ + // 动态路由将通过菜单 API 添加 + ] + } +] +``` + +**路由守卫逻辑:** + +```typescript +router.beforeEach(async (to, _from, next) => { + // 公众端路由直接放行 + if (to.path.startsWith("/p/")) { + next() + return + } + + const authStore = useAuthStore() + + // 未登录跳转登录页 + if (!authStore.token && to.meta.requiresAuth !== false) { + next({ name: "Login", query: { redirect: to.fullPath } }) + return + } + + // 已登录自动获取用户信息和菜单 + if (authStore.token && !authStore.user) { + await authStore.fetchUserInfo() + await addDynamicRoutes() // 添加动态路由 + } + + // 权限检查 + const requiredPermissions = to.meta.permissions + if (requiredPermissions && !authStore.hasAnyPermission(requiredPermissions)) { + next({ name: "Forbidden" }) + return + } + + next() +}) +``` + +--- + +### 3. 竞赛管理模块 (`contests/`) ⭐核心业务 + +**页面结构:** +``` +contests/ +├── Index.vue # 活动列表页 +├── Create.vue # 创建/编辑活动 +├── Detail.vue # 活动详情页 +├── SuperDetail.vue # 超管活动详情页 +├── RegisterIndividual.vue # 个人报名页 +├── RegisterTeam.vue # 团队报名页 +│ +├── components/ +│ ├── AddJudgeDrawer.vue # 添加评委抽屉 +│ ├── AddParticipantDrawer.vue # 添加参与者抽屉 +│ ├── AddTeacherDrawer.vue # 添加指导老师抽屉 +│ ├── SubmitWorkDrawer.vue # 提交作品抽屉 +│ ├── ViewWorkDrawer.vue # 查看作品抽屉 +│ └── WorkDetailModal.vue # 作品详情弹窗 +│ +├── judges/ +│ └── Index.vue # 评委管理 +│ +├── notices/ +│ └── Index.vue # 公告管理 +│ +├── registrations/ +│ ├── Index.vue # 报名管理列表 +│ └── Records.vue # 报名记录 +│ +├── reviews/ +│ ├── Index.vue # 评审管理 +│ ├── Progress.vue # 评审进度 +│ ├── ProgressDetail.vue # 评审进度详情 +│ └── Tasks.vue # 评审任务 +│ +├── results/ +│ ├── Index.vue # 成果管理列表 +│ └── Detail.vue # 成果发布详情 +│ +├── works/ +│ ├── Index.vue # 作品管理列表 +│ └── WorksDetail.vue # 参赛作品详情 +│ +└── Guidance.vue # 我的指导(教师视角) +``` + +#### 3.1 活动列表页 (`Index.vue`) + +**功能:** +- 活动列表展示(分页) +- 活动阶段筛选(未发布/报名中/提交中/评审中/已完结) +- 活动统计卡片 +- 创建活动入口 +- 活动状态管理(发布/完结/重新开启) + +#### 3.2 创建活动页 (`Create.vue`) + +**表单配置:** + +```typescript +interface CreateContestForm { + contestName: string; // 活动名称 + contestType: "individual" | "team"; // 活动类型 + + startTime: string; // 活动开始时间 + endTime: string; // 活动结束时间 + + // 报名配置 + registerStartTime: string; // 报名开始时间 + registerEndTime: string; // 报名结束时间 + requireAudit: boolean; // 是否需要审核 + teamMinMembers?: number; // 团队最少人数 + teamMaxMembers?: number; // 团队最多人数 + + // 作品配置 + submitStartTime: string; // 作品提交开始时间 + submitEndTime: string; // 作品提交结束时间 + submitRule: "once" | "resubmit"; // 提交规则 + workType: "image" | "video" | "document" | "code" | "other"; + + // 评审配置 + reviewStartTime: string; // 评审开始时间 + reviewEndTime: string; // 评审结束时间 + reviewRuleId?: number; // 评审规则 ID + + // 成果配置 + resultPublishTime?: string; // 结果发布时间 +} +``` + +#### 3.3 报名管理模块 + +**功能:** +- 报名列表(支持按状态筛选) +- 报名审核(通过/拒绝) +- 报名统计(待审核/已通过/已拒绝) +- 添加指导老师 +- 报名记录查看 + +#### 3.4 评审管理模块 + +**功能:** +- 评委分配(手动/批量/自动) +- 评审进度监控 +- 作品评分界面 +- 评审统计(评委进度/作品状态) + +**评审进度数据结构:** + +```typescript +interface ReviewProgress { + contest: { /* 活动信息 */ }; + summary: { + totalWorks: number; // 总作品数 + assignedWorksCount: number; // 已分配作品数 + scoredWorksCount: number; // 已评分作品数 + totalJudges: number; // 评委总数 + }; + progress: { + assignmentProgress: number; // 分配进度百分比 + scoringProgress: number; // 评分进度百分比 + overallProgress: number; // 整体进度百分比 + }; + judgeProgress: JudgeProgressItem[]; // 评委进度列表 + unassignedWorks: UnassignedWork[]; // 未分配作品列表 +} +``` + +#### 3.5 成果管理模块 + +**功能:** +- 计算最终得分(支持多种计算规则) +- 自动排名 +- 设置奖项(单个/批量/自动) +- 成果发布/撤回 + +**API 示例:** + +```typescript +// 自动设置奖项 +resultsApi.autoSetAwards(contestId, { + first: 3, // 一等奖 3 名 + second: 6, // 二等奖 6 名 + third: 10, // 三等奖 10 名 + excellent: 20 // 优秀奖 20 名 +}) + +// 批量设置奖项 +resultsApi.batchSetAwards(contestId, { + awards: [ + { workId: 1, awardLevel: 'first' }, + { workId: 2, awardLevel: 'second' }, + // ... + ] +}) +``` + +--- + +### 4. 用户管理模块 (`system/users/`) + +**页面:** `src/views/system/users/Index.vue` + +**功能:** +- 用户列表(分页 + 搜索) +- 用户类型筛选(平台/机构/评委/公共) +- 用户统计卡片 +- 创建/编辑用户 +- 用户状态切换(启用/禁用) +- 角色分配 + +**查询参数:** + +```typescript +interface UserQueryParams { + page?: number; + pageSize?: number; + keyword?: string; // 关键字搜索 + userType?: "platform" | "org" | "judge" | "public"; + filterTenantId?: number; // 按租户筛选 + userSource?: "admin_created" | "self_registered"; + status?: "enabled" | "disabled"; +} +``` + +**用户统计接口:** + +```typescript +interface UserStats { + total: number; // 总用户数 + platform: number; // 平台用户 + org: number; // 机构用户 + judge: number; // 评委用户 + public: number; // 公共用户 +} +``` + +--- + +### 5. AI 3D 创作模块 (`workbench/ai-3d/`) ⭐特色功能 + +**文件结构:** +``` +workbench/ai-3d/ +├── Index.vue # 3D 建模实验室首页 +├── Generate.vue # 模型生成页面 +└── History.vue # 创作历史 +``` + +**功能:** +- AI 文生 3D 模型(腾讯混元 API) +- 生成类型选择(Normal/LowPoly/Geometry/Sketch) +- 任务状态轮询 +- 多结果展示(4 个不同角度) +- 模型下载 +- 创作历史记录 + +**任务状态:** + +```typescript +type AI3DTaskStatus = "pending" | "processing" | "completed" | "failed" | "timeout"; + +interface AI3DTask { + id: number; + inputType: "text" | "image"; + inputContent: string; + status: AI3DTaskStatus; + resultUrls?: string[]; // 多结果 URL + previewUrls?: string[]; // 预览图 URL + queuePosition?: number; // 队列位置 + createTime: string; + completeTime?: string; +} +``` + +**生成参数:** + +```typescript +interface CreateAI3DTaskParams { + inputType: "text" | "image"; + inputContent: string; + generateType?: "Normal" | "LowPoly" | "Geometry" | "Sketch"; + faceCount?: number; // 模型面数 (10000-1500000) +} +``` + +--- + +### 6. 学校管理模块 (`school/`) + +**子模块:** +``` +school/ +├── schools/ # 学校信息 +├── grades/ # 年级管理 +├── classes/ # 班级管理 +├── teachers/ # 教师管理 +└── students/ # 学生管理 +``` + +**功能:** +- 学校信息 CRUD +- 年级管理(支持排序) +- 班级管理(关联年级) +- 教师列表(支持搜索) +- 学生列表(支持按班级筛选) + +--- + +### 7. 系统管理模块 (`system/`) + +**子模块:** + +| 页面 | 功能 | +|------|------| +| `users/` | 用户管理 | +| `roles/` | 角色管理 | +| `permissions/` | 权限管理 | +| `menus/` | 菜单管理 | +| `tenants/` | 租户管理 | +| `dict/` | 数据字典 | +| `config/` | 系统配置 | +| `logs/` | 系统日志 | + +--- + +### 8. 公众端模块 (`public/`) + +**页面结构:** +``` +public/ +├── Login.vue # 公众端登录 +├── Gallery.vue # 作品广场 +├── Activities.vue # 活动大厅 +├── ActivityDetail.vue # 活动详情 +│ +├── mine/ +│ ├── Index.vue # 个人中心 +│ ├── Registrations.vue # 我的报名 +│ ├── Works.vue # 我的作品 +│ └── Children.vue # 子女账号 +│ +├── create/ +│ ├── Index.vue # 绘本创作首页 +│ └── Generating.vue # 生成中页面 +│ +├── works/ +│ ├── Index.vue # 我的作品库 +│ ├── Detail.vue # 作品详情 +│ └── Publish.vue # 发布作品 +│ +└── components/ + └── WorkSelector.vue # 作品选择器 +``` + +**核心功能:** +- 公众端独立登录 +- 作品广场展示 +- 活动大厅浏览 +- 个人中心(报名/作品/子女管理) +- AI 绘本创作 + +--- + +### 9. 作业管理模块 (`homework/`) + +**页面:** +- `Index.vue` - 作业列表 +- `ReviewRules.vue` - 评审规则 +- `Submissions.vue` - 提交记录 +- `StudentDetail.vue` - 学生作业详情 +- `StudentList.vue` - 学生列表 + +**功能:** +- 作业发布与查看 +- 作业提交记录 +- 作业评审 +- 成绩查看 + +--- + +## 四、公共组件 + +### 1. RichTextEditor.vue - 富文本编辑器 + +**功能:** +- 基于 WangEditor +- 支持图文混排 +- 支持文件上传 +- 表单验证集成 + +**使用示例:** + +```vue + +``` + +### 2. ModelViewer.vue - 3D 模型预览 + +**功能:** +- 基于 Three.js +- 支持 GLB/GLTF 格式 +- 轨道控制器(旋转/缩放/平移) +- 自动加载/卸载 + +--- + +## 五、API 封装 + +### Axios 封装 (`utils/request.ts`) + +**核心特性:** +- 请求拦截器(自动添加 Token) +- 响应拦截器(统一错误处理) +- 多租户支持 +- TypeScript 泛型支持 + +**请求配置:** + +```typescript +const request = axios.create({ + baseURL: '/api', + timeout: 30000, +}) + +// 请求拦截器 +request.interceptors.request.use(config => { + const token = getToken() + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +// 响应拦截器 +request.interceptors.response.use( + response => response.data, + error => { + if (error.response?.status === 401) { + // Token 过期,跳转登录 + } + return Promise.reject(error) + } +) +``` + +### API 类型定义 + +```typescript +// types/api.ts +export interface PaginationParams { + page?: number + pageSize?: number +} + +export interface PaginationResponse { + list: T[] + total: number + page: number + pageSize: number +} +``` + +--- + +## 六、状态管理 + +### Auth Store (`stores/auth.ts`) + +**State:** +```typescript +{ + user: User | null, // 用户信息 + token: string, // JWT Token + menus: Menu[], // 用户菜单 + loading: boolean // 加载状态 +} +``` + +**Getters:** +```typescript +{ + isAuthenticated: boolean, // 是否已登录 + tenantCode: string, // 租户编码 + isSuperAdmin: () => boolean, // 是否超管 + hasPermission: (p: string) => boolean, // 权限检查 + hasAnyRole: (roles: string[]) => boolean // 角色检查 +} +``` + +**Actions:** +```typescript +{ + login: (form: LoginForm) => Promise, + logout: () => Promise, + fetchUserInfo: () => Promise, + fetchUserMenus: () => Promise, + updateToken: (newToken: string) => void, + initAuth: () => Promise, +} +``` + +--- + +## 七、组合式函数 (Composables) + +### useListRequest + +**用途:** 列表数据请求通用逻辑 + +**使用示例:** + +```typescript +const { + loading, + list, + total, + page, + pageSize, + fetchList, + handlePageChange, + handleSizeChange, +} = useListRequest(contestsApi.getList, initialParams) +``` + +### useSimpleListRequest + +**用途:** 简化版列表请求(无分页) + +--- + +## 八、自定义指令 + +### v-permission 权限指令 + +**使用示例:** + +```vue + +``` + +**实现:** + +```typescript +// directives/permission.ts +export const permission = { + mounted(el, binding) { + const { hasPermission } = useAuthStore() + const value = binding.value + const required = Array.isArray(value) ? value : [value] + + if (!hasPermission(required)) { + el.parentNode?.removeChild(el) + } + } +} +``` + +--- + +## 九、核心业务流程 + +### 1. 用户登录流程 + +``` +1. 输入用户名密码 → +2. 调用 login API → +3. 存储 Token(按租户隔离) → +4. 获取用户信息 → +5. 获取用户菜单 → +6. 添加动态路由 → +7. 跳转到首页 +``` + +### 2. 活动创建流程 + +``` +1. 填写活动基本信息 → +2. 配置报名参数 → +3. 配置作品参数 → +4. 配置评审规则 → +5. 提交创建 → +6. 跳转到活动详情页 +``` + +### 3. 作品提交流程 + +``` +1. 选择活动 → +2. 点击提交作品 → +3. 填写作品信息(标题/描述) → +4. 上传作品文件 → +5. 上传预览图 → +6. 提交成功 → +7. 可查看/编辑(在截止时间前) +``` + +### 4. 评审工作流程 + +``` +1. 管理员分配作品给评委 → +2. 评委登录后查看待评审作品 → +3. 查看作品详情 → +4. 按维度评分 → +5. 填写评语 → +6. 提交评分 +``` + +### 5. AI 3D 模型生成流程 + +``` +1. 输入提示词 → +2. 选择生成类型 → +3. 提交任务 → +4. 轮询任务状态 → +5. 生成完成后展示 4 个角度模型 → +6. 下载模型文件 +``` + +--- + +## 十、总结 + +### 项目特点 + +1. **完整的前后端分离架构** - Vue 3 + TypeScript + Vite +2. **动态路由系统** - 基于菜单动态加载路由 +3. **多租户支持** - 租户编码路径前缀 + Token 隔离 +4. **细粒度权限控制** - 权限指令 + 路由守卫 +5. **丰富的竞赛管理功能** - 从创建到评审全流程 +6. **AI 能力集成** - 腾讯混元 3D 模型生成 +7. **公众端独立入口** - /p 前缀区分 + +### 适用场景 + +- 📚 图书馆绘本创作比赛 +- 🏫 学校各类竞赛活动 +- 🎨 艺术创作比赛 +- 📖 作文/阅读比赛 +- 🤖 科技创新大赛 + +### 技术亮点 + +- Vue 3 Composition API +- TypeScript 类型安全 +- Pinia 状态管理 +- Ant Design Vue 组件库 +- Tailwind CSS 原子化 CSS +- Three.js 3D 渲染 +- WangEditor 富文本 + +--- + +> 文档生成时间:2026-03-28 +> 分析人:AI Assistant