From 87ece5a971b496e0efd50c569f4c05e440d758ef Mon Sep 17 00:00:00 2001
From: lovife <474239041@qq.com>
Date: Mon, 28 Sep 2026 20:49:55 +0800
Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E7=AE=A1=E7=90=86=E7=AB=AF?=
=?UTF-8?q?=E6=94=B9=E7=94=A8=20Gi=20Admin=20Pro=20=E6=A1=86=E6=9E=B6=20+?=
=?UTF-8?q?=20=E8=85=BE=E8=AE=AF=E4=BA=91=E4=B8=BB=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
前端此前只有本地实现、从未入库,本次首次纳入版本管理。
框架切换
· 以 Gi Admin Pro(Vue3 + Vite + TS + Arco Design Vue + Pinia)为底
· 剥离 14 个演示页面目录 / 121 个文件(demo 28、file 20、form 19、system 18…)
· 依赖 dependencies 35 → 19,devDependencies 31 → 27
· 保留框架核心:layout、Gi* 组件库、hooks、权限指令、错误页、登录页
主题:腾讯云 / TDesign 官方色板
· 主色 #0052D9(腾讯蓝)、成功 #00A870、警告 #ED7B2F、危险 #E34D59
· 页面底色 #f3f3f3、文字 rgba(0,0,0,90/60/40/26%)
· 覆盖 Arco 底层色板 + 显式语义别名(primary/success/warning/danger/link/neutral)
· 三个改了不生效也不报错的坑已记入注释:
1) Arco 色值是逗号分隔 RGB 三元组,不是 #RRGGBB
2) Arco 把色板定义在 body 而非 :root,必须写 `:root, body`
3) setting.json 的 themeColor 会以内联样式写进 body,优先级高于样式表
对接后端(三处契约差异在 @/utils/http 统一适配)
· 响应信封 {code,msg,data} → {code,success,message,data}
· Token 头 token → Authorization: Bearer
· 分页 {list,total} → 补一份 records,list 原样保留
· 40101 → 登录页;登录密码 RSA 加密(公钥走 /auth/public-key,轮换无需重建前端)
· 路由:后端只下发菜单键,前端在本地路由表上过滤(有哪些页面归代码,谁能看归后端)
已实现页面(浏览器实测通过)
· 登录:RSA 加密 + 自适应验证码/租户码 + 账号锁定提示
· 工作台:真实数据(组织 6 个 / 角色 1 个 / 权限 25 项)
· 组织架构:三级树 + CRUD + 占用校验
· 租户管理:开通租户 + 初始密码只显示一次的强确认
· 待建页:未实现菜单显示所属里程碑
· 权限菜单:学校管理员看不到「租户管理」
视觉资产按腾讯云主题重做
· logo.svg(渐变圆角方 + 填)、favicon.svg(小尺寸做减法)、login-hero.svg(材料归集)
· 登录页点阵纹理走主题变量,换主色自动跟随
· 文案改为产品实际能力:自动改名 / 强制压缩 / 隐私保护
清理模板遗留(约 17MB + 三处外部依赖)
· 百度统计脚本(上报到 gi-demo 作者的统计账号)
· 用户菜单「项目地址」(跳转模板作者的 Gitee 仓库)
· 两份 8.8MB mp3、388KB favicon.ico、57KB 模板插画、未引用字体与 loading.gif
· 「个人中心」指向已删除的 /system/account(点了 404)
· 头部头像在无 avatarUrl 时渲染成裂图 → 改为姓名首字兜底
后端修复
· 新增 GET /auth/public-key(前端加密登录密码用)
· DataSeeder.ancestor_path 生成错误:子节点本该是 /16/17/ 却写成 /17/
该字段是「按院系统计」的索引,错了会漏人/多人且不报错 —— 改为从库里读父节点路径
· seedDemoOrgs 曾放在租户作用域外 + admin 早退分支之后,导致启动失败 / 永不执行
· 示范租户补一套三级组织树(1 学校 / 2 院系 / 3 班级),供界面演示
---
.../fill/common/crypto/RsaCipher.java | 29 +
.../auth/controller/AuthController.java | 16 +
.../tianbaotong/fill/support/DataSeeder.java | 84 +-
frontend-pc/.agents/README.md | 29 +
frontend-pc/.agents/rules/agent.md | 36 +
frontend-pc/.agents/skills/code-css/SKILL.md | 36 +
frontend-pc/.agents/skills/code-ts/SKILL.md | 38 +
frontend-pc/.agents/skills/code-vue/SKILL.md | 67 +
.../.agents/skills/git-commit/SKILL.md | 132 +
.../.agents/skills/project-api/SKILL.md | 153 +
.../.agents/skills/project-crud/SKILL.md | 214 +
.../.agents/skills/project-guide/SKILL.md | 171 +
frontend-pc/.env.development | 10 +
frontend-pc/.env.production | 6 +
frontend-pc/.env.test | 5 +
frontend-pc/.github/workflows/deploy.yml | 82 +
frontend-pc/.gitignore | 30 +
frontend-pc/.stylelintignore | 31 +
frontend-pc/AGENTS.md | 51 +
frontend-pc/LICENSE | 201 +
frontend-pc/README.md | 622 ++
frontend-pc/eslint.config.js | 56 +
frontend-pc/index.html | 35 +
frontend-pc/package.json | 67 +
frontend-pc/pnpm-lock.yaml | 6565 +++++++++++++++++
frontend-pc/public/favicon.svg | 19 +
frontend-pc/public/static/css/loading.css | 55 +
frontend-pc/src/App.vue | 32 +
frontend-pc/src/apis/area/index.ts | 9 +
frontend-pc/src/apis/area/type.ts | 5 +
frontend-pc/src/apis/base.ts | 39 +
frontend-pc/src/apis/cate/index.ts | 9 +
frontend-pc/src/apis/cate/type.ts | 8 +
frontend-pc/src/apis/file/index.ts | 9 +
frontend-pc/src/apis/file/type.ts | 13 +
frontend-pc/src/apis/person/index.ts | 7 +
frontend-pc/src/apis/person/type.ts | 16 +
frontend-pc/src/apis/system/dept.ts | 15 +
frontend-pc/src/apis/system/dict.ts | 44 +
frontend-pc/src/apis/system/menu.ts | 39 +
frontend-pc/src/apis/system/role.ts | 23 +
frontend-pc/src/apis/system/user.ts | 24 +
frontend-pc/src/apis/test/index.ts | 11 +
frontend-pc/src/apis/user/index.ts | 24 +
frontend-pc/src/apis/user/type.ts | 34 +
frontend-pc/src/apis/xt/index.ts | 81 +
frontend-pc/src/apis/xt/org.ts | 63 +
frontend-pc/src/apis/xt/tenant.ts | 83 +
frontend-pc/src/assets/images/login-hero.svg | 77 +
frontend-pc/src/assets/images/logo.svg | 45 +
frontend-pc/src/auto-import.d.ts | 78 +
frontend-pc/src/components.d.ts | 160 +
.../src/components/GiButton/GiButton.vue | 86 +
frontend-pc/src/components/GiButton/index.ts | 5 +
frontend-pc/src/components/GiButton/type.ts | 5 +
.../src/components/GiCell/GiCellAvatar.vue | 31 +
.../src/components/GiCell/GiCellGender.vue | 28 +
.../src/components/GiCell/GiCellStatus.vue | 24 +
.../src/components/GiCell/GiCellTags.vue | 33 +
frontend-pc/src/components/GiCell/index.ts | 6 +
frontend-pc/src/components/GiDot/GiDot.tsx | 33 +
frontend-pc/src/components/GiDot/dot.scss | 58 +
frontend-pc/src/components/GiDot/index.ts | 5 +
frontend-pc/src/components/GiDot/type.ts | 10 +
.../components/GiEditTable/GiEditTable.vue | 176 +
.../src/components/GiEditTable/index.ts | 5 +
.../src/components/GiEditTable/type.ts | 81 +
frontend-pc/src/components/GiFlex/GiFlex.vue | 65 +
frontend-pc/src/components/GiFlex/index.ts | 5 +
frontend-pc/src/components/GiFlex/type.ts | 29 +
frontend-pc/src/components/GiForm/GiForm.vue | 273 +
frontend-pc/src/components/GiForm/index.ts | 5 +
frontend-pc/src/components/GiForm/type.ts | 299 +
.../src/components/GiIconBox/GiIconBox.vue | 99 +
frontend-pc/src/components/GiIconBox/index.ts | 5 +
frontend-pc/src/components/GiIconBox/type.ts | 6 +
.../GiInputSearch/GiInputSearch.vue | 50 +
.../src/components/GiInputSearch/index.ts | 5 +
.../src/components/GiInputSearch/type.ts | 5 +
.../src/components/GiOption/GiOption.vue | 16 +
.../src/components/GiOption/GiOptionItem.vue | 79 +
frontend-pc/src/components/GiOption/index.ts | 7 +
frontend-pc/src/components/GiOption/type.ts | 11 +
.../components/GiPageLayout/GiPageLayout.vue | 192 +
.../components/GiPageLayout/SplitButton.vue | 100 +
.../src/components/GiPageLayout/index.ts | 5 +
.../src/components/GiPageLayout/type.ts | 14 +
.../src/components/GiTable/GiTable.vue | 440 ++
frontend-pc/src/components/GiTable/index.ts | 5 +
frontend-pc/src/components/GiTable/type.ts | 53 +
.../GiTableSetting/GiTableSetting.vue | 291 +
.../src/components/GiTableSetting/index.ts | 5 +
.../src/components/GiTableSetting/type.ts | 22 +
.../GiTableSetting/useTableColumnSetting.ts | 141 +
frontend-pc/src/components/GiTag/GiTag.tsx | 135 +
frontend-pc/src/components/GiTag/index.ts | 5 +
frontend-pc/src/components/GiTag/tag.scss | 166 +
frontend-pc/src/components/GiTag/type.ts | 13 +
.../src/components/GiThemeBtn/GiThemeBtn.vue | 16 +
.../src/components/GiThemeBtn/index.ts | 4 +
.../components/dialog/TreeUserList/Tree.vue | 45 +
.../components/dialog/TreeUserList/index.vue | 93 +
.../src/components/dialog/UserList/index.vue | 89 +
frontend-pc/src/components/dialog/index.ts | 16 +
frontend-pc/src/components/icons/Icon403.vue | 625 ++
frontend-pc/src/components/icons/Icon404.vue | 363 +
frontend-pc/src/components/icons/Icon500.vue | 967 +++
frontend-pc/src/components/index.ts | 20 +
frontend-pc/src/config/setting.json | 11 +
frontend-pc/src/config/xt-routes.ts | 230 +
frontend-pc/src/directives/hasPerm.ts | 44 +
frontend-pc/src/directives/hasRole.ts | 44 +
frontend-pc/src/directives/index.ts | 10 +
frontend-pc/src/hooks/app/index.ts | 2 +
frontend-pc/src/hooks/app/useDept.ts | 27 +
frontend-pc/src/hooks/app/useRole.ts | 21 +
frontend-pc/src/hooks/index.ts | 11 +
frontend-pc/src/hooks/useBreakpoint.ts | 34 +
frontend-pc/src/hooks/useChart.ts | 30 +
frontend-pc/src/hooks/useDevice.ts | 17 +
frontend-pc/src/hooks/useDict.ts | 37 +
frontend-pc/src/hooks/useLoading.ts | 30 +
frontend-pc/src/hooks/usePagination.ts | 90 +
frontend-pc/src/hooks/useRequest.ts | 90 +
frontend-pc/src/hooks/useResetReactive.ts | 47 +
frontend-pc/src/hooks/useRouteListener.ts | 92 +
frontend-pc/src/hooks/useTable.ts | 277 +
frontend-pc/src/hooks/useTheme.ts | 75 +
frontend-pc/src/icons/custom-icons.json | 197 +
frontend-pc/src/icons/index.ts | 6 +
frontend-pc/src/layout/LayoutColumns.vue | 77 +
frontend-pc/src/layout/LayoutDefault.vue | 33 +
frontend-pc/src/layout/LayoutMix.vue | 165 +
frontend-pc/src/layout/LayoutTop.vue | 57 +
.../src/layout/components/Asider/index.vue | 79 +
.../layout/components/Breadcrumb/index.vue | 130 +
.../src/layout/components/Header/index.vue | 46 +
.../components/HeaderRightBar/Notice.vue | 85 +
.../HeaderRightBar/SettingDrawerPanel.vue | 168 +
.../components/LayoutModeItem.vue | 270 +
.../components/HeaderRightBar/index.vue | 184 +
frontend-pc/src/layout/components/Logo.vue | 85 +
frontend-pc/src/layout/components/Main.vue | 37 +
.../src/layout/components/Menu/MenuIcon.vue | 21 +
.../src/layout/components/Menu/MenuItem.vue | 44 +
.../src/layout/components/MenuFoldBtn.vue | 92 +
.../layout/components/OneLevelMenu/index.vue | 108 +
.../src/layout/components/Tabs/MagicIcon.vue | 99 +
.../src/layout/components/Tabs/ReloadIcon.vue | 46 +
.../src/layout/components/Tabs/index.vue | 238 +
frontend-pc/src/layout/hooks/useLevelMenu.ts | 123 +
frontend-pc/src/layout/hooks/useMenu.ts | 101 +
frontend-pc/src/layout/index.vue | 45 +
frontend-pc/src/main.ts | 39 +
frontend-pc/src/router/guard/index.ts | 31 +
.../src/router/guard/setupPermissionGuard.ts | 89 +
frontend-pc/src/router/index.ts | 99 +
frontend-pc/src/stores/index.ts | 14 +
frontend-pc/src/stores/useAppStore.ts | 42 +
frontend-pc/src/stores/useDictStore.ts | 66 +
frontend-pc/src/stores/useMenuBadgeStore.ts | 34 +
frontend-pc/src/stores/useRouteStore.ts | 177 +
frontend-pc/src/stores/useTabsStore.ts | 250 +
frontend-pc/src/stores/useUserStore.ts | 164 +
frontend-pc/src/styles/animated.scss | 23 +
frontend-pc/src/styles/arco-ui/a-alert.less | 3 +
frontend-pc/src/styles/arco-ui/a-card.less | 1 +
.../src/styles/arco-ui/a-color-picker.less | 19 +
.../src/styles/arco-ui/a-descriptions.less | 11 +
frontend-pc/src/styles/arco-ui/a-form.less | 28 +
.../src/styles/arco-ui/a-input-number.less | 70 +
frontend-pc/src/styles/arco-ui/a-input.less | 7 +
frontend-pc/src/styles/arco-ui/a-menu.less | 18 +
frontend-pc/src/styles/arco-ui/a-message.less | 28 +
frontend-pc/src/styles/arco-ui/a-modal.less | 44 +
.../src/styles/arco-ui/a-page-header.less | 6 +
.../src/styles/arco-ui/a-pagination.less | 41 +
frontend-pc/src/styles/arco-ui/a-picker.less | 3 +
frontend-pc/src/styles/arco-ui/a-radio.less | 3 +
.../src/styles/arco-ui/a-scrollbar.less | 21 +
frontend-pc/src/styles/arco-ui/a-space.less | 0
frontend-pc/src/styles/arco-ui/a-steps.less | 5 +
frontend-pc/src/styles/arco-ui/a-switch.less | 7 +
frontend-pc/src/styles/arco-ui/a-table.less | 9 +
frontend-pc/src/styles/arco-ui/a-tabs.less | 17 +
frontend-pc/src/styles/arco-ui/a-tree.less | 23 +
frontend-pc/src/styles/arco-ui/index.less | 46 +
frontend-pc/src/styles/arco-ui/xt-theme.less | 237 +
frontend-pc/src/styles/base.scss | 53 +
frontend-pc/src/styles/css/transition.css | 163 +
frontend-pc/src/styles/editor.scss | 37 +
frontend-pc/src/styles/global.scss | 335 +
frontend-pc/src/styles/index.scss | 17 +
frontend-pc/src/styles/mixin.scss | 21 +
frontend-pc/src/styles/nprogress.scss | 4 +
frontend-pc/src/styles/scrollbar-reset.scss | 22 +
frontend-pc/src/styles/var.scss | 3 +
frontend-pc/src/types/api.d.ts | 13 +
frontend-pc/src/types/app.d.ts | 14 +
frontend-pc/src/types/env.d.ts | 12 +
frontend-pc/src/types/global.d.ts | 23 +
frontend-pc/src/types/module.d.ts | 3 +
frontend-pc/src/types/router.d.ts | 43 +
frontend-pc/src/types/shims-vue.d.ts | 8 +
frontend-pc/src/types/tool.d.ts | 14 +
frontend-pc/src/utils/auth.ts | 39 +
frontend-pc/src/utils/createSelectDialog.ts | 103 +
frontend-pc/src/utils/downloadFile.ts | 76 +
frontend-pc/src/utils/file.ts | 71 +
frontend-pc/src/utils/http.ts | 216 +
frontend-pc/src/utils/index.ts | 297 +
frontend-pc/src/utils/mitt.ts | 10 +
.../utils/modules/getFieldToRangTimeForm.ts | 46 +
.../utils/modules/getRangeTimeToFieldForm.ts | 45 +
frontend-pc/src/utils/permission.ts | 80 +
frontend-pc/src/utils/regexp.ts | 127 +
frontend-pc/src/utils/rsa.ts | 57 +
frontend-pc/src/utils/typeof.ts | 179 +
frontend-pc/src/utils/validate.ts | 10 +
frontend-pc/src/views/coming-soon/index.vue | 71 +
frontend-pc/src/views/dashboard/index.vue | 253 +
frontend-pc/src/views/error/403.vue | 11 +
frontend-pc/src/views/error/404.vue | 11 +
frontend-pc/src/views/error/500.vue | 11 +
.../src/views/error/components/ErrorPage.vue | 163 +
frontend-pc/src/views/login/LoginLeft.vue | 286 +
frontend-pc/src/views/login/LoginPalette.vue | 100 +
frontend-pc/src/views/login/index.vue | 414 ++
frontend-pc/src/views/org/OrgFormModal.vue | 162 +
frontend-pc/src/views/org/index.vue | 270 +
frontend-pc/src/views/redirect/index.vue | 13 +
frontend-pc/src/views/tenant/index.vue | 394 +
frontend-pc/stylelint.config.js | 44 +
frontend-pc/tsconfig.json | 40 +
frontend-pc/vite.config.ts | 115 +
scripts/dev-clean-e2e.py | 68 +-
scripts/extract-icons.py | 86 +
237 files changed, 25215 insertions(+), 26 deletions(-)
create mode 100644 frontend-pc/.agents/README.md
create mode 100644 frontend-pc/.agents/rules/agent.md
create mode 100644 frontend-pc/.agents/skills/code-css/SKILL.md
create mode 100644 frontend-pc/.agents/skills/code-ts/SKILL.md
create mode 100644 frontend-pc/.agents/skills/code-vue/SKILL.md
create mode 100644 frontend-pc/.agents/skills/git-commit/SKILL.md
create mode 100644 frontend-pc/.agents/skills/project-api/SKILL.md
create mode 100644 frontend-pc/.agents/skills/project-crud/SKILL.md
create mode 100644 frontend-pc/.agents/skills/project-guide/SKILL.md
create mode 100644 frontend-pc/.env.development
create mode 100644 frontend-pc/.env.production
create mode 100644 frontend-pc/.env.test
create mode 100644 frontend-pc/.github/workflows/deploy.yml
create mode 100644 frontend-pc/.gitignore
create mode 100644 frontend-pc/.stylelintignore
create mode 100644 frontend-pc/AGENTS.md
create mode 100644 frontend-pc/LICENSE
create mode 100644 frontend-pc/README.md
create mode 100644 frontend-pc/eslint.config.js
create mode 100644 frontend-pc/index.html
create mode 100644 frontend-pc/package.json
create mode 100644 frontend-pc/pnpm-lock.yaml
create mode 100644 frontend-pc/public/favicon.svg
create mode 100644 frontend-pc/public/static/css/loading.css
create mode 100644 frontend-pc/src/App.vue
create mode 100644 frontend-pc/src/apis/area/index.ts
create mode 100644 frontend-pc/src/apis/area/type.ts
create mode 100644 frontend-pc/src/apis/base.ts
create mode 100644 frontend-pc/src/apis/cate/index.ts
create mode 100644 frontend-pc/src/apis/cate/type.ts
create mode 100644 frontend-pc/src/apis/file/index.ts
create mode 100644 frontend-pc/src/apis/file/type.ts
create mode 100644 frontend-pc/src/apis/person/index.ts
create mode 100644 frontend-pc/src/apis/person/type.ts
create mode 100644 frontend-pc/src/apis/system/dept.ts
create mode 100644 frontend-pc/src/apis/system/dict.ts
create mode 100644 frontend-pc/src/apis/system/menu.ts
create mode 100644 frontend-pc/src/apis/system/role.ts
create mode 100644 frontend-pc/src/apis/system/user.ts
create mode 100644 frontend-pc/src/apis/test/index.ts
create mode 100644 frontend-pc/src/apis/user/index.ts
create mode 100644 frontend-pc/src/apis/user/type.ts
create mode 100644 frontend-pc/src/apis/xt/index.ts
create mode 100644 frontend-pc/src/apis/xt/org.ts
create mode 100644 frontend-pc/src/apis/xt/tenant.ts
create mode 100644 frontend-pc/src/assets/images/login-hero.svg
create mode 100644 frontend-pc/src/assets/images/logo.svg
create mode 100644 frontend-pc/src/auto-import.d.ts
create mode 100644 frontend-pc/src/components.d.ts
create mode 100644 frontend-pc/src/components/GiButton/GiButton.vue
create mode 100644 frontend-pc/src/components/GiButton/index.ts
create mode 100644 frontend-pc/src/components/GiButton/type.ts
create mode 100644 frontend-pc/src/components/GiCell/GiCellAvatar.vue
create mode 100644 frontend-pc/src/components/GiCell/GiCellGender.vue
create mode 100644 frontend-pc/src/components/GiCell/GiCellStatus.vue
create mode 100644 frontend-pc/src/components/GiCell/GiCellTags.vue
create mode 100644 frontend-pc/src/components/GiCell/index.ts
create mode 100644 frontend-pc/src/components/GiDot/GiDot.tsx
create mode 100644 frontend-pc/src/components/GiDot/dot.scss
create mode 100644 frontend-pc/src/components/GiDot/index.ts
create mode 100644 frontend-pc/src/components/GiDot/type.ts
create mode 100644 frontend-pc/src/components/GiEditTable/GiEditTable.vue
create mode 100644 frontend-pc/src/components/GiEditTable/index.ts
create mode 100644 frontend-pc/src/components/GiEditTable/type.ts
create mode 100644 frontend-pc/src/components/GiFlex/GiFlex.vue
create mode 100644 frontend-pc/src/components/GiFlex/index.ts
create mode 100644 frontend-pc/src/components/GiFlex/type.ts
create mode 100644 frontend-pc/src/components/GiForm/GiForm.vue
create mode 100644 frontend-pc/src/components/GiForm/index.ts
create mode 100644 frontend-pc/src/components/GiForm/type.ts
create mode 100644 frontend-pc/src/components/GiIconBox/GiIconBox.vue
create mode 100644 frontend-pc/src/components/GiIconBox/index.ts
create mode 100644 frontend-pc/src/components/GiIconBox/type.ts
create mode 100644 frontend-pc/src/components/GiInputSearch/GiInputSearch.vue
create mode 100644 frontend-pc/src/components/GiInputSearch/index.ts
create mode 100644 frontend-pc/src/components/GiInputSearch/type.ts
create mode 100644 frontend-pc/src/components/GiOption/GiOption.vue
create mode 100644 frontend-pc/src/components/GiOption/GiOptionItem.vue
create mode 100644 frontend-pc/src/components/GiOption/index.ts
create mode 100644 frontend-pc/src/components/GiOption/type.ts
create mode 100644 frontend-pc/src/components/GiPageLayout/GiPageLayout.vue
create mode 100644 frontend-pc/src/components/GiPageLayout/SplitButton.vue
create mode 100644 frontend-pc/src/components/GiPageLayout/index.ts
create mode 100644 frontend-pc/src/components/GiPageLayout/type.ts
create mode 100644 frontend-pc/src/components/GiTable/GiTable.vue
create mode 100644 frontend-pc/src/components/GiTable/index.ts
create mode 100644 frontend-pc/src/components/GiTable/type.ts
create mode 100644 frontend-pc/src/components/GiTableSetting/GiTableSetting.vue
create mode 100644 frontend-pc/src/components/GiTableSetting/index.ts
create mode 100644 frontend-pc/src/components/GiTableSetting/type.ts
create mode 100644 frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts
create mode 100644 frontend-pc/src/components/GiTag/GiTag.tsx
create mode 100644 frontend-pc/src/components/GiTag/index.ts
create mode 100644 frontend-pc/src/components/GiTag/tag.scss
create mode 100644 frontend-pc/src/components/GiTag/type.ts
create mode 100644 frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue
create mode 100644 frontend-pc/src/components/GiThemeBtn/index.ts
create mode 100644 frontend-pc/src/components/dialog/TreeUserList/Tree.vue
create mode 100644 frontend-pc/src/components/dialog/TreeUserList/index.vue
create mode 100644 frontend-pc/src/components/dialog/UserList/index.vue
create mode 100644 frontend-pc/src/components/dialog/index.ts
create mode 100644 frontend-pc/src/components/icons/Icon403.vue
create mode 100644 frontend-pc/src/components/icons/Icon404.vue
create mode 100644 frontend-pc/src/components/icons/Icon500.vue
create mode 100644 frontend-pc/src/components/index.ts
create mode 100644 frontend-pc/src/config/setting.json
create mode 100644 frontend-pc/src/config/xt-routes.ts
create mode 100644 frontend-pc/src/directives/hasPerm.ts
create mode 100644 frontend-pc/src/directives/hasRole.ts
create mode 100644 frontend-pc/src/directives/index.ts
create mode 100644 frontend-pc/src/hooks/app/index.ts
create mode 100644 frontend-pc/src/hooks/app/useDept.ts
create mode 100644 frontend-pc/src/hooks/app/useRole.ts
create mode 100644 frontend-pc/src/hooks/index.ts
create mode 100644 frontend-pc/src/hooks/useBreakpoint.ts
create mode 100644 frontend-pc/src/hooks/useChart.ts
create mode 100644 frontend-pc/src/hooks/useDevice.ts
create mode 100644 frontend-pc/src/hooks/useDict.ts
create mode 100644 frontend-pc/src/hooks/useLoading.ts
create mode 100644 frontend-pc/src/hooks/usePagination.ts
create mode 100644 frontend-pc/src/hooks/useRequest.ts
create mode 100644 frontend-pc/src/hooks/useResetReactive.ts
create mode 100644 frontend-pc/src/hooks/useRouteListener.ts
create mode 100644 frontend-pc/src/hooks/useTable.ts
create mode 100644 frontend-pc/src/hooks/useTheme.ts
create mode 100644 frontend-pc/src/icons/custom-icons.json
create mode 100644 frontend-pc/src/icons/index.ts
create mode 100644 frontend-pc/src/layout/LayoutColumns.vue
create mode 100644 frontend-pc/src/layout/LayoutDefault.vue
create mode 100644 frontend-pc/src/layout/LayoutMix.vue
create mode 100644 frontend-pc/src/layout/LayoutTop.vue
create mode 100644 frontend-pc/src/layout/components/Asider/index.vue
create mode 100644 frontend-pc/src/layout/components/Breadcrumb/index.vue
create mode 100644 frontend-pc/src/layout/components/Header/index.vue
create mode 100644 frontend-pc/src/layout/components/HeaderRightBar/Notice.vue
create mode 100644 frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue
create mode 100644 frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue
create mode 100644 frontend-pc/src/layout/components/HeaderRightBar/index.vue
create mode 100644 frontend-pc/src/layout/components/Logo.vue
create mode 100644 frontend-pc/src/layout/components/Main.vue
create mode 100644 frontend-pc/src/layout/components/Menu/MenuIcon.vue
create mode 100644 frontend-pc/src/layout/components/Menu/MenuItem.vue
create mode 100644 frontend-pc/src/layout/components/MenuFoldBtn.vue
create mode 100644 frontend-pc/src/layout/components/OneLevelMenu/index.vue
create mode 100644 frontend-pc/src/layout/components/Tabs/MagicIcon.vue
create mode 100644 frontend-pc/src/layout/components/Tabs/ReloadIcon.vue
create mode 100644 frontend-pc/src/layout/components/Tabs/index.vue
create mode 100644 frontend-pc/src/layout/hooks/useLevelMenu.ts
create mode 100644 frontend-pc/src/layout/hooks/useMenu.ts
create mode 100644 frontend-pc/src/layout/index.vue
create mode 100644 frontend-pc/src/main.ts
create mode 100644 frontend-pc/src/router/guard/index.ts
create mode 100644 frontend-pc/src/router/guard/setupPermissionGuard.ts
create mode 100644 frontend-pc/src/router/index.ts
create mode 100644 frontend-pc/src/stores/index.ts
create mode 100644 frontend-pc/src/stores/useAppStore.ts
create mode 100644 frontend-pc/src/stores/useDictStore.ts
create mode 100644 frontend-pc/src/stores/useMenuBadgeStore.ts
create mode 100644 frontend-pc/src/stores/useRouteStore.ts
create mode 100644 frontend-pc/src/stores/useTabsStore.ts
create mode 100644 frontend-pc/src/stores/useUserStore.ts
create mode 100644 frontend-pc/src/styles/animated.scss
create mode 100644 frontend-pc/src/styles/arco-ui/a-alert.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-card.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-color-picker.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-descriptions.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-form.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-input-number.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-input.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-menu.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-message.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-modal.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-page-header.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-pagination.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-picker.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-radio.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-scrollbar.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-space.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-steps.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-switch.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-table.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-tabs.less
create mode 100644 frontend-pc/src/styles/arco-ui/a-tree.less
create mode 100644 frontend-pc/src/styles/arco-ui/index.less
create mode 100644 frontend-pc/src/styles/arco-ui/xt-theme.less
create mode 100644 frontend-pc/src/styles/base.scss
create mode 100644 frontend-pc/src/styles/css/transition.css
create mode 100644 frontend-pc/src/styles/editor.scss
create mode 100644 frontend-pc/src/styles/global.scss
create mode 100644 frontend-pc/src/styles/index.scss
create mode 100644 frontend-pc/src/styles/mixin.scss
create mode 100644 frontend-pc/src/styles/nprogress.scss
create mode 100644 frontend-pc/src/styles/scrollbar-reset.scss
create mode 100644 frontend-pc/src/styles/var.scss
create mode 100644 frontend-pc/src/types/api.d.ts
create mode 100644 frontend-pc/src/types/app.d.ts
create mode 100644 frontend-pc/src/types/env.d.ts
create mode 100644 frontend-pc/src/types/global.d.ts
create mode 100644 frontend-pc/src/types/module.d.ts
create mode 100644 frontend-pc/src/types/router.d.ts
create mode 100644 frontend-pc/src/types/shims-vue.d.ts
create mode 100644 frontend-pc/src/types/tool.d.ts
create mode 100644 frontend-pc/src/utils/auth.ts
create mode 100644 frontend-pc/src/utils/createSelectDialog.ts
create mode 100644 frontend-pc/src/utils/downloadFile.ts
create mode 100644 frontend-pc/src/utils/file.ts
create mode 100644 frontend-pc/src/utils/http.ts
create mode 100644 frontend-pc/src/utils/index.ts
create mode 100644 frontend-pc/src/utils/mitt.ts
create mode 100644 frontend-pc/src/utils/modules/getFieldToRangTimeForm.ts
create mode 100644 frontend-pc/src/utils/modules/getRangeTimeToFieldForm.ts
create mode 100644 frontend-pc/src/utils/permission.ts
create mode 100644 frontend-pc/src/utils/regexp.ts
create mode 100644 frontend-pc/src/utils/rsa.ts
create mode 100644 frontend-pc/src/utils/typeof.ts
create mode 100644 frontend-pc/src/utils/validate.ts
create mode 100644 frontend-pc/src/views/coming-soon/index.vue
create mode 100644 frontend-pc/src/views/dashboard/index.vue
create mode 100644 frontend-pc/src/views/error/403.vue
create mode 100644 frontend-pc/src/views/error/404.vue
create mode 100644 frontend-pc/src/views/error/500.vue
create mode 100644 frontend-pc/src/views/error/components/ErrorPage.vue
create mode 100644 frontend-pc/src/views/login/LoginLeft.vue
create mode 100644 frontend-pc/src/views/login/LoginPalette.vue
create mode 100644 frontend-pc/src/views/login/index.vue
create mode 100644 frontend-pc/src/views/org/OrgFormModal.vue
create mode 100644 frontend-pc/src/views/org/index.vue
create mode 100644 frontend-pc/src/views/redirect/index.vue
create mode 100644 frontend-pc/src/views/tenant/index.vue
create mode 100644 frontend-pc/stylelint.config.js
create mode 100644 frontend-pc/tsconfig.json
create mode 100644 frontend-pc/vite.config.ts
create mode 100644 scripts/extract-icons.py
diff --git a/backend/xt-common/src/main/java/cn/tianbaotong/fill/common/crypto/RsaCipher.java b/backend/xt-common/src/main/java/cn/tianbaotong/fill/common/crypto/RsaCipher.java
index a9cd232..12f8b91 100644
--- a/backend/xt-common/src/main/java/cn/tianbaotong/fill/common/crypto/RsaCipher.java
+++ b/backend/xt-common/src/main/java/cn/tianbaotong/fill/common/crypto/RsaCipher.java
@@ -4,7 +4,10 @@ import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
/**
@@ -40,6 +43,32 @@ public final class RsaCipher {
}
}
+ /**
+ * 导出对应的公钥(PEM,X.509/SPKI)。
+ *
+ *
用于 {@code GET /api/v1/auth/public-key}:前端启动时拉一次,
+ * 之后用它加密登录密码。
+ *
+ *
为什么不把公钥打包进前端:一是密钥轮换时前端要重新构建、重新发布;
+ * 二是打包进去意味着"改密钥"这件事横跨前后端两个仓库,
+ * 实际操作中一定会出现"后端换了、前端没换"导致全员登不进去。
+ * 公钥是公开信息,放接口上没有任何安全损失。
+ */
+ public String publicKeyPem() {
+ try {
+ if (!(privateKey instanceof RSAPrivateCrtKey crt)) {
+ throw new IllegalStateException("私钥不是 CRT 格式,无法推导公钥");
+ }
+ RSAPublicKey pub = (RSAPublicKey) KeyFactory.getInstance("RSA")
+ .generatePublic(new RSAPublicKeySpec(crt.getModulus(), crt.getPublicExponent()));
+ String b64 = Base64.getMimeEncoder(64, new byte[]{'\n'})
+ .encodeToString(pub.getEncoded());
+ return "-----BEGIN PUBLIC KEY-----\n" + b64 + "\n-----END PUBLIC KEY-----\n";
+ } catch (Exception e) {
+ throw new IllegalStateException("导出 RSA 公钥失败", e);
+ }
+ }
+
/**
* 解密前端传来的密码。
*
diff --git a/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/auth/controller/AuthController.java b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/auth/controller/AuthController.java
index ed81dd0..a1dd1de 100644
--- a/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/auth/controller/AuthController.java
+++ b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/auth/controller/AuthController.java
@@ -1,6 +1,7 @@
package cn.tianbaotong.fill.module.auth.controller;
import cn.tianbaotong.fill.common.api.ApiResult;
+import cn.tianbaotong.fill.common.crypto.RsaCipher;
import cn.tianbaotong.fill.module.auth.dto.LoginReq;
import cn.tianbaotong.fill.module.auth.dto.LoginVO;
import cn.tianbaotong.fill.module.auth.service.AuthService;
@@ -29,6 +30,7 @@ public class AuthController {
private final AuthService authService;
private final CaptchaService captchaService;
+ private final RsaCipher rsaCipher;
@PublicApi
@PostMapping("/login")
@@ -44,6 +46,20 @@ public class AuthController {
return ApiResult.ok(captchaService.generate());
}
+ /**
+ * 登录密码加密用的公钥。
+ *
+ *
公钥是公开信息,无需鉴权。前端启动时拉一次并缓存,
+ * 这样轮换密钥不需要重新构建前端——否则"后端换了、前端没换"
+ * 会表现为"所有人密码都错",而且很难定位。
+ */
+ @PublicApi
+ @GetMapping("/public-key")
+ @Operation(summary = "获取登录加密公钥", description = "PEM 格式,前端用它与 RSA/ECB/PKCS1Padding 加密密码")
+ public ApiResult publicKey() {
+ return ApiResult.ok(rsaCipher.publicKeyPem());
+ }
+
@GetMapping("/me")
@Operation(summary = "当前用户", description = "返回用户、租户、角色、菜单与细粒度权限码")
public ApiResult me() {
diff --git a/backend/xt-server/src/main/java/cn/tianbaotong/fill/support/DataSeeder.java b/backend/xt-server/src/main/java/cn/tianbaotong/fill/support/DataSeeder.java
index af286fd..2fd446f 100644
--- a/backend/xt-server/src/main/java/cn/tianbaotong/fill/support/DataSeeder.java
+++ b/backend/xt-server/src/main/java/cn/tianbaotong/fill/support/DataSeeder.java
@@ -2,6 +2,8 @@ package cn.tianbaotong.fill.support;
import cn.tianbaotong.fill.common.context.LoginUser;
import cn.tianbaotong.fill.common.context.UserContext;
+import cn.tianbaotong.fill.module.org.entity.SysOrg;
+import cn.tianbaotong.fill.module.org.mapper.OrgMapper;
import cn.tianbaotong.fill.module.tenant.entity.SysTenant;
import cn.tianbaotong.fill.module.tenant.mapper.TenantMapper;
import cn.tianbaotong.fill.module.user.entity.SysUser;
@@ -44,6 +46,7 @@ public class DataSeeder implements ApplicationRunner {
private final TenantMapper tenantMapper;
private final UserMapper userMapper;
+ private final OrgMapper orgMapper;
private final RoleProvisioner roleProvisioner;
private final PasswordEncoder passwordEncoder;
@@ -102,6 +105,15 @@ public class DataSeeder implements ApplicationRunner {
TenantScope.runAs(tid, 0L, Set.of(BuiltinRole.SCHOOL_ADMIN.getCode()), () -> {
roleProvisioner.provisionBuiltinRoles(tid);
+
+ // ⚠️ 必须放在 runAs 内部:sys_org 有 tenant_id,
+ // 在租户作用域外查询会触发拦截器的「默认拒绝」直接抛 40101,
+ // 而且它发生在启动阶段(ApplicationRunner),表现是应用起不来。
+ //
+ // ⚠️ 也必须放在下面那个「admin 已存在就早退」之前:
+ // 否则第二次启动之后组织树永远不会被补上(自身已幂等,重复调用无害)。
+ seedDemoOrgs(tid);
+
if (findUser(tid, "admin") != null) {
return;
}
@@ -118,16 +130,76 @@ public class DataSeeder implements ApplicationRunner {
});
}
+ /**
+ * 示范组织树。
+ *
+ * 空的组织树看不出系统在做什么,所以给个有真实感的三级结构
+ * (学校 → 院系 → 班级)。名字取自客户的真实班级命名习惯(24中技数控1班)。
+ *
+ *
幂等:只要该租户已有任何组织就跳过,不覆盖用户自己建的。
+ */
+ private void seedDemoOrgs(final long tenantId) {
+ Long existing = orgMapper.selectCount(Wrappers.lambdaQuery());
+ if (existing != null && existing > 0) {
+ return;
+ }
+
+ TenantScope.runAs(tenantId, 0L, Set.of(BuiltinRole.SCHOOL_ADMIN.getCode()), () -> {
+ long school = insertOrg(0L, "示范职业学校", 1, "DEMO", 1);
+
+ long mech = insertOrg(school, "智能制造系", 2, "MECH", 1);
+ insertOrg(mech, "24中技数控1班", 3, "24ZK1", 1);
+ insertOrg(mech, "24中技机电1班", 3, "24JD1", 2);
+
+ long cs = insertOrg(school, "信息工程系", 2, "CS", 2);
+ insertOrg(cs, "24中技计算机1班", 3, "24JSJ1", 1);
+
+ log.info("已为示范租户创建组织树(1 个学校 / 2 个院系 / 3 个班级)");
+ });
+ }
+
+ /**
+ * 插入一个组织节点并回填 {@code ancestor_path}。
+ *
+ * ⚠️ 路径形如 {@code /10/11/12/},**必须包含全部祖先**,末位才是自己。
+ * 这不是显示用的字段 —— 「按院系统计」靠 {@code LIKE '/10/%'} 一次覆盖所有后代,
+ * 路径错了统计就会漏人/多人,而且不会报错。
+ *
+ *
路径依赖自身 id,只能先插入再回写;上级路径从库里查(不靠入参传),
+ * 避免调用方漏传或传错 —— 之前就是因为把 parentPath 传成 null,
+ * 导致所有子节点的路径都退化成了只有自己。
+ */
+ private long insertOrg(Long parentId, String name, int type, String code, int sortNo) {
+ SysOrg o = new SysOrg();
+ o.setParentId(parentId);
+ o.setOrgName(name);
+ o.setOrgType(type);
+ o.setOrgCode(code);
+ o.setSortNo(sortNo);
+ o.setStatus(1);
+ orgMapper.insert(o);
+
+ String parentPath = "/";
+ if (parentId != null && parentId != 0L) {
+ SysOrg parent = orgMapper.selectById(parentId);
+ if (parent == null || parent.getAncestorPath() == null) {
+ throw new IllegalStateException("上级组织的层级路径缺失:" + parentId);
+ }
+ parentPath = parent.getAncestorPath();
+ }
+ String path = parentPath + o.getId() + "/";
+
+ SysOrg patch = new SysOrg();
+ patch.setId(o.getId());
+ patch.setAncestorPath(path);
+ orgMapper.updateById(patch);
+ return o.getId();
+ }
+
private SysUser findUser(long tenantId, String username) {
return userMapper.selectOne(Wrappers.lambdaQuery()
.eq(SysUser::getTenantId, tenantId)
.eq(SysUser::getUsername, username)
.last("LIMIT 1"));
}
-
- /** 保留:主应用启动时用到的常量,避免未使用告警。 */
- @SuppressWarnings("unused")
- private static List noop() {
- return List.of();
- }
}
diff --git a/frontend-pc/.agents/README.md b/frontend-pc/.agents/README.md
new file mode 100644
index 0000000..65b0e12
--- /dev/null
+++ b/frontend-pc/.agents/README.md
@@ -0,0 +1,29 @@
+# .agents/
+
+本目录为 AI 编码助手规范的**唯一来源**(Single Source of Truth),适用于 Cursor、OpenCode、Codex 等工具。
+
+## 结构
+
+```
+.agents/
+├── rules/
+│ └── agent.md # 唯一常驻规则(Cursor 经 AGENTS.md 内联注入)
+└── skills/ # 按需加载
+ ├── code-ts/SKILL.md
+ ├── code-vue/SKILL.md
+ ├── code-css/SKILL.md
+ ├── project-guide/SKILL.md
+ ├── project-crud/SKILL.md
+ ├── project-api/SKILL.md
+ └── git-commit/SKILL.md
+```
+
+## 工具适配
+
+| 工具 | 适配方式 |
+|------|----------|
+| Cursor | `AGENTS.md`(内联 behavior 规则 + 技能索引)+ `.agents/skills/`(按需) |
+| OpenCode | `AGENTS.md` + `opencode.json` + `.agents/rules/` + `.agents/skills/` |
+| Codex | `AGENTS.md` + `.agents/skills/` |
+
+修改 `agent.md` 后请同步 `AGENTS.md` 的「常驻规则」一节;编码规范改对应 skill,不要重复写入 `AGENTS.md`。
diff --git a/frontend-pc/.agents/rules/agent.md b/frontend-pc/.agents/rules/agent.md
new file mode 100644
index 0000000..01550ae
--- /dev/null
+++ b/frontend-pc/.agents/rules/agent.md
@@ -0,0 +1,36 @@
+# Agent 行为准则
+
+> 适用于 Cursor、OpenCode、Codex 等 AI 编码助手。Cursor 经 `AGENTS.md` 内联本节每轮注入。
+
+## 通用要求
+
+- 始终使用**简体中文**与用户沟通。
+- 未经用户明确要求,**不要**创建说明文档、执行 git commit/push、修改 git 配置。
+- 改动范围最小化:只改与任务直接相关的代码,不顺手重构无关模块。
+- 优先复用项目已有抽象与约定,禁止凭空引入新架构或重复造轮子。
+
+## 应当
+
+- 改代码前先阅读项目既有实现,保持风格一致。
+- 优先复用项目内已有组件、composables、工具函数与 API 封装。
+- 任务完成后,视情况运行 lint / typecheck / test(与改动相关时)。
+- **按需读取** skills,不要预加载全部;写代码前按任务类型加载对应 skill(见 `AGENTS.md` 技能索引)。
+
+## 不应
+
+- 为已自动导入的 API / 组件写冗余 import。
+- 引入与项目技术栈不符的 UI 库或全新抽象层。
+- 猜测不存在的 API 路径、响应结构或配置项;以项目代码与类型定义为准。
+- 在用户未要求时添加测试、文档或无关注释。
+
+## 编码前必读 skill
+
+| 任务 | 技能 |
+|------|------|
+| 写/改 `.vue` | `code-vue` |
+| 写/改样式 | `code-css` |
+| 写/改 `.ts` / composable | `code-ts` |
+| 项目约定、Gi 组件 | `project-guide` |
+| CRUD 列表/表单 | `project-crud` |
+| API / Mock | `project-api` |
+| 提交代码 | `git-commit` |
diff --git a/frontend-pc/.agents/skills/code-css/SKILL.md b/frontend-pc/.agents/skills/code-css/SKILL.md
new file mode 100644
index 0000000..b9bcffa
--- /dev/null
+++ b/frontend-pc/.agents/skills/code-css/SKILL.md
@@ -0,0 +1,36 @@
+---
+name: code-css
+description: Vue SFC 样式块、BEM 命名、Stylelint 与 scoped 约定。编写或修改组件样式(.vue style、scss、less、css)时加载本技能。
+---
+
+# CSS 规范(Vue 3)
+
+## SFC 样式块
+
+- 位于 `
+```
+
+## useTable 用法
+
+```ts
+import type * as T from '@/apis/system/user'
+import { baseAPI } from '@/apis/system/user'
+import { useDict, useTable } from '@/hooks'
+
+const { dictData } = useDict(['STATUS'] as const)
+
+const queryParams = reactive({ status: '', username: '' })
+
+const {
+ loading,
+ tableData,
+ pagination,
+ selectedKeys,
+ search,
+ select,
+ selectAll,
+ fixed, // 操作列 fixed: 'right'(小屏自动取消)
+ onDelete,
+ onBatchDelete,
+ refresh
+} = useTable({
+ listAPI: (page) => baseAPI.getList({ ...page, ...queryParams }),
+ deleteAPI: (ids) => baseAPI.delete({ ids }),
+ immediate: true,
+ rowKey: 'id', // 默认 id
+ crossPageSelect: false // 跨页多选时设为 true
+})
+```
+
+要点:
+
+- `search()` 会重置到第 1 页并清空选中
+- `refresh()` 保持当前页刷新
+- `listAPI` 参数为 `{ page, size }`,需展开到接口
+- 空字符串查询条件建议转 `undefined` 再传 API
+
+## GiButton 预设类型
+
+`add` | `edit` | `delete` | `search` | `reset` | `upload` | `import` | `export`
+
+列表工具栏与操作列统一使用 `GiButton`,操作列常用 `size="mini"`。
+
+## 表格列(TSX)
+
+使用 `lang="tsx"`,从 `@arco-design/web-vue` 引入 `TableColumnData`:
+
+```tsx
+const tableColumns: TableColumnData[] = [
+ {
+ title: '序号',
+ width: 68,
+ align: 'center',
+ render: ({ rowIndex }) => {rowIndex + 1}
+ },
+ { title: '名称', dataIndex: 'name', ellipsis: true, tooltip: true },
+ {
+ title: '状态',
+ width: 100,
+ align: 'center',
+ render: ({ record }) => (
+
+ )
+ },
+ {
+ title: '操作',
+ width: 160,
+ align: 'center',
+ fixed: 'right', // 或绑定 useTable 的 fixed
+ render: ({ record }) => (
+
+ onEdit(record as T.ListItem)} />
+ onDelete(record as T.ListItem)}>
+
+
+
+ )
+ }
+]
+```
+
+常用单元格组件:`GiCellAvatar`、`GiCellGender`、`GiCellStatus`(从 `@/components/GiCell` 引入或在 TSX 中直接使用)。
+
+## GiPageLayout
+
+| 属性/插槽 | 说明 |
+|-----------|------|
+| `#left` | 左侧栏(部门树、字典类型列表等) |
+| `#header` | 顶栏(少用) |
+| `margin` | 外边距模式 |
+| `collapsed` | 显示折叠按钮 |
+| `size` | 左侧宽度,如 `"280px"` |
+
+左栏选中后通过事件同步右侧列表,参考 `dict/index.vue` 的 `onDictTypeRefresh`。
+
+## 表单弹窗(Modal + GiForm)
+
+```vue
+
+
+
+```
+
+```ts
+import type { FormColumnItem } from '@/components/index'
+import { useResetReactive } from '@/hooks'
+
+const [form, resetForm] = useResetReactive({ id: '', name: '', status: '1' as Status })
+const GiFormRef = useTemplateRef>('GiFormRef')
+
+const add = () => {
+ detailId.value = ''
+ visible.value = true
+}
+const edit = async (id: string) => {
+ visible.value = true
+ detailId.value = id
+ Object.assign(form, (await baseAPI.getDetail({ id })).data)
+}
+const close = () => {
+ GiFormRef.value?.formRef?.resetFields()
+ resetForm()
+}
+const save = async () => {
+ const valid = await GiFormRef.value?.formRef?.validate()
+ if (valid) return false
+ // await baseAPI.add(form) 或 baseAPI.update(form)
+ emit('save-success')
+ return true
+}
+
+defineExpose({ add, edit })
+```
+
+`formColumns` 常用 `type`:`input`、`select`、`tree-select`、`radio-group`、`switch`、`textarea`。校验可用 `required`、`rules`;正则见 `@/utils/regexp`。
+
+## 字典下拉
+
+```ts
+const { dictData } = useDict(['STATUS'] as const)
+// 模板:
+```
+
+## 表格增强方案选型
+
+| 需求 | 方案 |
+|------|------|
+| 内置斑马纹/全屏/列设置 | `GiTable` |
+| 继续用 `a-table` + 工具栏/列设置 | `GiTableSetting` 包裹(见 `views/table/custom3`) |
+| 简单列表 | `a-table` + `useTable`(系统模块主流) |
+
+## 检查清单
+
+- [ ] `defineOptions({ name })` 已设置
+- [ ] `row-key="id"` 与 `useTable` 的 `rowKey` 一致
+- [ ] 删除走 `onDelete` / `onBatchDelete` 或自定义 `Popconfirm` + API
+- [ ] 弹窗 `defineExpose({ add, edit })`,父组件 `useTemplateRef` 调用
+- [ ] 保存成功后 `emit('save-success')` 并触发 `search`
diff --git a/frontend-pc/.agents/skills/project-guide/SKILL.md b/frontend-pc/.agents/skills/project-guide/SKILL.md
new file mode 100644
index 0000000..c75edd1
--- /dev/null
+++ b/frontend-pc/.agents/skills/project-guide/SKILL.md
@@ -0,0 +1,171 @@
+---
+name: project-guide
+description: 后台管理模板的项目总览与前端强制规范。涵盖 Vue 3 + Arco Design + Gi 组件库、目录结构、自动导入、CRUD/API 约定与代码风格。在当前仓库内开发、新增页面、查阅架构或不确定项目规范时使用。
+---
+
+# 项目开发指南
+
+> **适用范围**:本仓库 `src/**`、`mock/**`
+> **规范来源**:`AGENTS.md` 常驻行为准则 + 本 skill(项目细节);编码规范按需读 `code-vue` / `code-css` / `code-ts` skill
+
+## 本项目 Agent 约定
+- 优先复用 `Gi*`、`hooks`、`getBaseApi`、现有 mock 工具;新增页面/接口对齐参考页与 `getBaseApi` 约定。
+- API / Mock 以 `src/apis`、`mock` 与类型定义为准,不要猜测路径。
+- 涉及 CRUD、API、Mock 时按需读取对应 skill;写 `.vue` / 样式 / TS 前读取 [code-vue](../code-vue/SKILL.md)、[code-css](../code-css/SKILL.md)、[code-ts](../code-ts/SKILL.md)。
+- 改动后视情况运行 `pnpm typecheck`、`pnpm lint`、`pnpm lint:style`。
+
+## 技术栈
+
+| 类别 | 选型 |
+|------|------|
+| 框架 | Vue 3.5 + TypeScript + `
+```
+
+```vue
+
+```
+
+```vue
+
+```
+
+```vue
+
+```
+
+#### 写法技巧
+
+尽量使用三元表达式
+
+```js
+// 优化前
+let isEdit = true
+let title = ''
+if (isEdit) {
+ title = '编辑'
+} else {
+ title = '新增'
+}
+
+// 优化后
+let title = isEdit ? '编辑' : '新增'
+```
+
+善用 includes 方法
+
+```js
+// 优化前
+if (type === 1 || type === 2 || type === 3) {
+}
+
+// 优化后,此种方式在vue模板也可使用
+if ([1, 2, 3].includes(type)) {
+}
+```
+
+使用箭头函数简化函数
+
+```js
+// 优化前
+function add(num1, num2) {
+ return num1 + num2
+}
+
+// 优化后
+const add = (num1, num2) => num1 + num2
+```
+
+尽量减少 if else if
+
+```vue
+
+```
+
+```js
+// 优化前
+const status = 200
+const message = ''
+if (status === 200) {
+ message = '请求成功'
+} else if (status === 404) {
+ message = '请求出错'
+} else if (status === 500) {
+ message = '服务器错误'
+}
+
+// 优化后
+const status = 200
+const messageMap = {
+ 200: '请求成功',
+ 404: '请求出错',
+ 500: '服务器错误'
+}
+const message = messageMap[status]
+```
+
+如果函数参数超过两个,建议优化
+
+```vue
+
+```
+
+#### 接口调用书写
+
+**写法一**
+
+不需要 loading,不需要错误打印的 情况
+
+```vue
+
+
+// getUserListApi 是一个 Promise 异步函数,Promise 最后只有 成功 / 失败 两种状态 // getUserListApi 是基于 axios
+封装的,在 axios 响应拦截器做了处理 // 当 res.success === false 的时候 Promise.reject(),
+也就异步失败,异步失败不会往下执行
+```
+
+**写法二**
+
+需要 loading,需要错误打印的情况
+
+```vue
+
+```
+
+**写法三**
+
+需要 loading, 不需要错误打印(不进行错误处理)的情况
+
+```vue
+
+
+// catch 可以省略
+```
+
+#### 全局组件--命名规范
+
+组件命名:**单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)**
+
+可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/
+
+```
+GiForm.vue
+GiTable.vue
+GiPageLayout.vue
+```
+
+#### 局部组件--命名规范
+
+组件命名:**单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)**
+
+可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/
+
+```
+Pane1.vue
+Pane2.vue
+PaneQuota1.vue
+PaneQuota2.vue
+Step1.vue
+Step2.vue
+AddModal.vue
+EditDrawer.vue
+DetailModal.vue
+```
+
+#### 文件夹命名--命名规范 (采用中划线-)
+
+1、文件名建议只使用小写字母,不使用大写字母
+
+2、名称较长时采用半角连接符(-)分隔
+
+```
+home/index.vue
+user/index.vue
+user-detail/index.vue
+```
+
+#### 弹窗组件 Modal、抽屉组件 Drawer 的一般封装
+
+```vue
+
+
+
+
+
+
+
+```
+
+使用
+
+**`模板里使用自定义组件:使用大写开头驼峰,双击好复制,对于搜索便利`**
+
+```vue
+
+
+
+
+
+```
+
+#### CSS 命名规范
+
+建议采用全小写,多单词使用-连接符(参考大部分网站,包括掘金,码云等,都是采用这个规则)
+
+或者采用`BEM`命名规范 [BEM 命名规范](https://getbem.com/naming/)
+
+**BEM 命名规范**
+
+```html
+
+```
+
+
+其他:
+
+位置 1: 使用 var(--margin) 全局 css 变量
+
+位置 2:使用 var(--padding) 全局 css 变量
+
+建议尽量使用全局 css 变量来开发,可以有效提高效率和团队协作
+
+
+
+
+
+#### 其他规范
+
+可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/ , 其中一些规范也可借鉴
+
+可参考 **Gi Admin Pro** 源码,如有更好的规范建议,可以联系作者本人
+
+## Vue 相关
+
+Vue3 官网
+
+Vue-Router
+
+Vite
+
+Pinia
+
+## 插件推荐
+
+Arco Design 组件库
+
+Day.js 一个极简的 JavaScript 库,可以为现代浏览器解析、验证、操作和显示日期和时间 2K 大小
+
+Lodash 一个一致性、模块化、高性能的 JavaScript 实用工具库
+
+Xe-utils 一个 JavaScript 函数库、工具类
+
+VueUse 一个 Vue3 Hooks 库
+
+VueRequest 一个 Vue 请求库
+
+V-Viewer 基于 viewer.js 的 vue 图像查看器组件,支持旋转、缩放等操作
+
+Vue-Color-Kit 一个 vue3 颜色选择器组件
+
+Vxe-Table
+
+vue3-request 一个专为 Vue 3 设计的异步请求处理库,它通过统一的 useRequest Hook 和强大的插件生态,彻底简化了异步操作和 API 调用的复杂性
+
+**其他**
+
+VCalendar 日历组件
+
+Vue Cal 日历组件
+
+VueDraggablePlus 支持 Vue2 和 Vue3 的拖拽组件
+
+## 推荐书籍
+
+Vue 入门指南与实战案例
+
+深入理解 TypeScript
+
+前端宝典
+
+Web 开发人员需要知道的 CSS 技巧
+
+阮一峰 ES6
+
+阮一峰 flex 布局
+
+## 开源项目集合
+
+Vue3 开源项目集合
+
+React 开源项目集合
+
+## 工具
+
+菜鸟工具
+
+代码生成图片
+
+JSON-TO-ANY 对象转 ts 类型
+
+在线三角形样式生成器
+
+iTab
+
+## 捐赠
+
+
diff --git a/frontend-pc/eslint.config.js b/frontend-pc/eslint.config.js
new file mode 100644
index 0000000..e6520fe
--- /dev/null
+++ b/frontend-pc/eslint.config.js
@@ -0,0 +1,56 @@
+import antfu from '@antfu/eslint-config'
+
+// https://github.com/antfu/eslint-config
+export default antfu(
+ {
+ vue: true,
+ typescript: true,
+ ignores: [
+ 'README.md',
+ 'src/types/shims-vue.d.ts'
+ ]
+ },
+ {
+ // Remember to specify the file glob here, otherwise it might cause the vue plugin to handle non-vue files
+ files: ['**/*.vue'],
+ rules: {
+ 'vue/block-order': [2, {
+ order: [['script', 'template'], 'style']
+ }], // 强制组件顶级元素的顺序
+ 'vue/html-self-closing': [0, {
+ html: {
+ void: 'never',
+ normal: 'always',
+ component: 'never'
+ }
+ }], // 强制自结束样式
+ 'vue/custom-event-name-casing': [2, 'kebab-case'], // 对自定义事件名称强制使用特定大小写
+ 'vue/singleline-html-element-content-newline': 0, // 要求在单行元素的内容前后换行
+ 'vue/first-attribute-linebreak': 0, // 强制第一个属性的位置
+ 'vue/define-macros-order': [2, {
+ order: ['defineOptions', 'defineModel', 'defineProps', 'defineEmits', 'defineSlots'],
+ defineExposeLast: true
+ }], // 强制执行定义限制和定义弹出编译器宏的顺序
+ 'vue/html-indent': 0, // 在《模板》中强制一致的缩进
+ 'vue/html-closing-bracket-newline': 0 // 要求或不允许在标记的右括号前换行
+ }
+ },
+ {
+ // Without `files`, they are general rules for all files
+ rules: {
+ 'curly': [0, 'all'], // 对所有控制语句强制使用一致的大括号样式
+ 'dot-notation': 0, // 尽可能强制使用点表示法。 在 JavaScript 中,可以使用点表示法 (foo.bar) 或方括号表示法 (foo["bar"]) 访问属性
+ 'no-new': 0, // 不允许在赋值或比较之外使用 new 运算符
+ // 'no-console': 2, // 禁止使用 console
+ 'no-process-env': 0,
+ 'style/arrow-parens': [2, 'always'], // 箭头函数参数需要括号
+ 'style/brace-style': [2, '1tbs', { allowSingleLine: true }], // 对块执行一致的大括号样式
+ 'style/comma-dangle': [2, 'never'], // 要求或不允许尾随逗号
+ 'ts/consistent-type-definitions': 0,
+ 'ts/no-unused-expressions': 0,
+ 'node/prefer-global/process': 0,
+ 'antfu/top-level-function': 0,
+ 'antfu/if-newline': 0
+ }
+ }
+)
diff --git a/frontend-pc/index.html b/frontend-pc/index.html
new file mode 100644
index 0000000..4466c27
--- /dev/null
+++ b/frontend-pc/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+ 填报通 · 管理端
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/package.json b/frontend-pc/package.json
new file mode 100644
index 0000000..3bb942b
--- /dev/null
+++ b/frontend-pc/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "xt-frontend-pc",
+ "type": "module",
+ "version": "1.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "build-tsc": "vue-tsc --noEmit && vite build",
+ "preview": "vite preview --port 5050",
+ "typecheck": "vue-tsc --noEmit",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "lint:style": "stylelint \"**/*.{vue,scss,css}\" --cache",
+ "lint:style:fix": "stylelint \"**/*.{vue,scss,css}\" --cache --fix"
+ },
+ "dependencies": {
+ "@arco-design/color": "^0.4.0",
+ "@iconify/vue": "^5.0.0",
+ "@vueuse/core": "^13.6.0",
+ "animate.css": "^4.1.1",
+ "axios": "^0.27.2",
+ "dayjs": "^1.11.4",
+ "echarts": "^6.0.0",
+ "jsencrypt": "^3.5.4",
+ "lodash-es": "^4.17.21",
+ "mitt": "^3.0.1",
+ "nprogress": "^0.2.0",
+ "pinia": "^3.0.3",
+ "pinia-plugin-persistedstate": "^4.4.1",
+ "sortablejs": "^1.15.6",
+ "vue": "^3.5.18",
+ "vue-color-kit": "^1.0.6",
+ "vue-draggable-plus": "^0.6.0",
+ "vue-router": "^4.5.1",
+ "xe-utils": "^3.7.8"
+ },
+ "devDependencies": {
+ "@antfu/eslint-config": "^7.0.1",
+ "@arco-design/web-vue": "^2.58.0",
+ "@iconify-json/icon-park-outline": "^1.2.4",
+ "@types/lodash-es": "^4.17.12",
+ "@types/node": "^24.2.0",
+ "@vitejs/plugin-vue": "^6.0.1",
+ "@vitejs/plugin-vue-jsx": "^5.0.1",
+ "@vue/tsconfig": "^0.7.0",
+ "eslint": "^9.5.0",
+ "less": "^4.4.0",
+ "postcss": "^8.5.6",
+ "postcss-html": "^1.8.0",
+ "sass": "^1.62.1",
+ "stylelint": "^16.25.0",
+ "stylelint-config-recess-order": "^7.4.0",
+ "stylelint-config-recommended-vue": "^1.6.1",
+ "stylelint-config-standard": "^39.0.1",
+ "stylelint-config-standard-scss": "^16.0.0",
+ "stylelint-scss": "^6.12.1",
+ "typescript": "^5.9.2",
+ "unplugin-auto-import": "^20.0.0",
+ "unplugin-icons": "^22.5.0",
+ "unplugin-vue-components": "^29.0.0",
+ "vite": "^7.0.6",
+ "vite-plugin-compression": "^0.5.1",
+ "vite-plugin-vue-devtools": "^8.0.5",
+ "vue-tsc": "^3.0.5"
+ },
+ "description": "数据信息填报系统 · PC 管理端(管理员 / 班主任)"
+}
diff --git a/frontend-pc/pnpm-lock.yaml b/frontend-pc/pnpm-lock.yaml
new file mode 100644
index 0000000..8c67240
--- /dev/null
+++ b/frontend-pc/pnpm-lock.yaml
@@ -0,0 +1,6565 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@arco-design/color':
+ specifier: ^0.4.0
+ version: 0.4.0
+ '@iconify/vue':
+ specifier: ^5.0.0
+ version: 5.0.3(vue@3.5.23(typescript@5.9.3))
+ '@vueuse/core':
+ specifier: ^13.6.0
+ version: 13.9.0(vue@3.5.23(typescript@5.9.3))
+ animate.css:
+ specifier: ^4.1.1
+ version: 4.1.1
+ axios:
+ specifier: ^0.27.2
+ version: 0.27.2
+ dayjs:
+ specifier: ^1.11.4
+ version: 1.11.19
+ echarts:
+ specifier: ^6.0.0
+ version: 6.1.0
+ jsencrypt:
+ specifier: ^3.5.4
+ version: 3.5.4
+ lodash-es:
+ specifier: ^4.17.21
+ version: 4.17.21
+ mitt:
+ specifier: ^3.0.1
+ version: 3.0.1
+ nprogress:
+ specifier: ^0.2.0
+ version: 0.2.0
+ pinia:
+ specifier: ^3.0.3
+ version: 3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))
+ pinia-plugin-persistedstate:
+ specifier: ^4.4.1
+ version: 4.7.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3)))
+ sortablejs:
+ specifier: ^1.15.6
+ version: 1.15.6
+ vue:
+ specifier: ^3.5.18
+ version: 3.5.23(typescript@5.9.3)
+ vue-color-kit:
+ specifier: ^1.0.6
+ version: 1.0.6(vue@3.5.23(typescript@5.9.3))
+ vue-draggable-plus:
+ specifier: ^0.6.0
+ version: 0.6.0(@types/sortablejs@1.15.9)
+ vue-router:
+ specifier: ^4.5.1
+ version: 4.6.3(vue@3.5.23(typescript@5.9.3))
+ xe-utils:
+ specifier: ^3.7.8
+ version: 3.7.9
+ devDependencies:
+ '@antfu/eslint-config':
+ specifier: ^7.0.1
+ version: 7.7.3(@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3))(@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3))(@vue/compiler-sfc@3.5.23)(eslint@9.39.1)(typescript@5.9.3)
+ '@arco-design/web-vue':
+ specifier: ^2.58.0
+ version: 2.58.0(vue@3.5.23(typescript@5.9.3))
+ '@iconify-json/icon-park-outline':
+ specifier: ^1.2.4
+ version: 1.2.4
+ '@types/lodash-es':
+ specifier: ^4.17.12
+ version: 4.17.12
+ '@types/node':
+ specifier: ^24.2.0
+ version: 24.10.0
+ '@vitejs/plugin-vue':
+ specifier: ^6.0.1
+ version: 6.0.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))
+ '@vitejs/plugin-vue-jsx':
+ specifier: ^5.0.1
+ version: 5.1.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))
+ '@vue/tsconfig':
+ specifier: ^0.7.0
+ version: 0.7.0(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))
+ eslint:
+ specifier: ^9.5.0
+ version: 9.39.1
+ less:
+ specifier: ^4.4.0
+ version: 4.4.2
+ postcss:
+ specifier: ^8.5.6
+ version: 8.5.6
+ postcss-html:
+ specifier: ^1.8.0
+ version: 1.8.0
+ sass:
+ specifier: ^1.62.1
+ version: 1.93.3
+ stylelint:
+ specifier: ^16.25.0
+ version: 16.25.0(typescript@5.9.3)
+ stylelint-config-recess-order:
+ specifier: ^7.4.0
+ version: 7.4.0(stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)))(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-config-recommended-vue:
+ specifier: ^1.6.1
+ version: 1.6.1(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-config-standard:
+ specifier: ^39.0.1
+ version: 39.0.1(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-config-standard-scss:
+ specifier: ^16.0.0
+ version: 16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-scss:
+ specifier: ^6.12.1
+ version: 6.12.1(stylelint@16.25.0(typescript@5.9.3))
+ typescript:
+ specifier: ^5.9.2
+ version: 5.9.3
+ unplugin-auto-import:
+ specifier: ^20.0.0
+ version: 20.2.0(@vueuse/core@13.9.0(vue@3.5.23(typescript@5.9.3)))
+ unplugin-icons:
+ specifier: ^22.5.0
+ version: 22.5.0(@vue/compiler-sfc@3.5.23)
+ unplugin-vue-components:
+ specifier: ^29.0.0
+ version: 29.2.0(@babel/parser@7.28.5)(vue@3.5.23(typescript@5.9.3))
+ vite:
+ specifier: ^7.0.6
+ version: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vite-plugin-compression:
+ specifier: ^0.5.1
+ version: 0.5.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+ vite-plugin-vue-devtools:
+ specifier: ^8.0.5
+ version: 8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))
+ vue-tsc:
+ specifier: ^3.0.5
+ version: 3.1.3(typescript@5.9.3)
+
+packages:
+
+ '@antfu/eslint-config@7.7.3':
+ resolution: {integrity: sha512-BtroDxTvmWtvr3yJkdWVCvwsKlnEdkreoeOyrdNezc/W5qaiQNf2xjcsQ3N5Yy0x27h+0WFfW8rG8YlVioG6dw==}
+ hasBin: true
+ peerDependencies:
+ '@angular-eslint/eslint-plugin': ^21.1.0
+ '@angular-eslint/eslint-plugin-template': ^21.1.0
+ '@angular-eslint/template-parser': ^21.1.0
+ '@eslint-react/eslint-plugin': ^2.11.0
+ '@next/eslint-plugin-next': '>=15.0.0'
+ '@prettier/plugin-xml': ^3.4.1
+ '@unocss/eslint-plugin': '>=0.50.0'
+ astro-eslint-parser: ^1.0.2
+ eslint: ^9.10.0 || ^10.0.0
+ eslint-plugin-astro: ^1.2.0
+ eslint-plugin-format: '>=0.1.0'
+ eslint-plugin-jsx-a11y: '>=6.10.2'
+ eslint-plugin-react-hooks: ^7.0.0
+ eslint-plugin-react-refresh: ^0.5.0
+ eslint-plugin-solid: ^0.14.3
+ eslint-plugin-svelte: '>=2.35.1'
+ eslint-plugin-vuejs-accessibility: ^2.4.1
+ prettier-plugin-astro: ^0.14.0
+ prettier-plugin-slidev: ^1.0.5
+ svelte-eslint-parser: '>=0.37.0'
+ peerDependenciesMeta:
+ '@angular-eslint/eslint-plugin':
+ optional: true
+ '@angular-eslint/eslint-plugin-template':
+ optional: true
+ '@angular-eslint/template-parser':
+ optional: true
+ '@eslint-react/eslint-plugin':
+ optional: true
+ '@next/eslint-plugin-next':
+ optional: true
+ '@prettier/plugin-xml':
+ optional: true
+ '@unocss/eslint-plugin':
+ optional: true
+ astro-eslint-parser:
+ optional: true
+ eslint-plugin-astro:
+ optional: true
+ eslint-plugin-format:
+ optional: true
+ eslint-plugin-jsx-a11y:
+ optional: true
+ eslint-plugin-react-hooks:
+ optional: true
+ eslint-plugin-react-refresh:
+ optional: true
+ eslint-plugin-solid:
+ optional: true
+ eslint-plugin-svelte:
+ optional: true
+ eslint-plugin-vuejs-accessibility:
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-slidev:
+ optional: true
+ svelte-eslint-parser:
+ optional: true
+
+ '@antfu/install-pkg@1.1.0':
+ resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
+ '@antfu/install-pkg@2.1.0':
+ resolution: {integrity: sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==}
+
+ '@arco-design/color@0.4.0':
+ resolution: {integrity: sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==}
+
+ '@arco-design/web-vue@2.58.0':
+ resolution: {integrity: sha512-b1vdPYOmjG5VAkVa7jlVwCb+WynBK+rnKN8zH3yKohpZObZbostRd3HgYNtjjZjGVU3OqR0Yy2FX7ftgF0bcOw==}
+ peerDependencies:
+ vue: '>=3.1.0'
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.5':
+ resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.5':
+ resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.5':
+ resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-create-class-features-plugin@7.28.5':
+ resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-member-expression-to-functions@7.28.5':
+ resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-replace-supers@7.27.1':
+ resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-proposal-decorators@7.28.0':
+ resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-decorators@7.27.1':
+ resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.28.5':
+ resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.5':
+ resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
+ engines: {node: '>=6.9.0'}
+
+ '@cacheable/memoize@2.0.3':
+ resolution: {integrity: sha512-hl9wfQgpiydhQEIv7fkjEzTGE+tcosCXLKFDO707wYJ/78FVOlowb36djex5GdbSyeHnG62pomYLMuV/OT8Pbw==}
+
+ '@cacheable/memory@2.0.4':
+ resolution: {integrity: sha512-cCmJKCKlT1t7hNBI1+gFCwmKFd9I4pS3zqBeNGXTSODnpa0EeDmORHY8oEMTuozfdg3cgsVh8ojLaPYb6eC7Cg==}
+
+ '@cacheable/utils@2.2.0':
+ resolution: {integrity: sha512-7xaQayO3msdVcxXLYcLU5wDqJBNdQcPPPHr6mdTEIQI7N7TbtSVVTpWOTfjyhg0L6AQwQdq7miKdWtTDBoBldQ==}
+
+ '@clack/core@1.5.1':
+ resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==}
+ engines: {node: '>= 20.12.0'}
+
+ '@clack/prompts@1.8.1':
+ resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==}
+ engines: {node: '>= 20.12.0'}
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+ engines: {node: '>=18'}
+
+ '@csstools/media-query-list-parser@4.0.3':
+ resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/selector-specificity@5.0.0':
+ resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ postcss-selector-parser: ^7.0.0
+
+ '@dual-bundle/import-meta-resolve@4.2.1':
+ resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==}
+
+ '@e18e/eslint-plugin@0.2.0':
+ resolution: {integrity: sha512-mXgODVwhuDjTJ+UT+XSvmMmCidtGKfrV5nMIv1UtpWex2pYLsIM3RSpT8HWIMAebS9qANbXPKlSX4BE7ZvuCgA==}
+ peerDependencies:
+ eslint: ^9.0.0 || ^10.0.0
+ oxlint: ^1.41.0
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+ oxlint:
+ optional: true
+
+ '@es-joy/jsdoccomment@0.86.0':
+ resolution: {integrity: sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@es-joy/jsdoccomment@0.88.0':
+ resolution: {integrity: sha512-GK/HL/claLLNo5KG705auIlZMwEtmn88ofSGuLsmVZwKBqMPJhW9DiznYNq07QEqz9BPtA3LBfYImtZmhVvRAw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@es-joy/resolve.exports@1.2.0':
+ resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==}
+ engines: {node: '>=10'}
+
+ '@esbuild/aix-ppc64@0.25.12':
+ resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.12':
+ resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.12':
+ resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.12':
+ resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.12':
+ resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.12':
+ resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.12':
+ resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.12':
+ resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.12':
+ resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.12':
+ resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.12':
+ resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.12':
+ resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.12':
+ resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.12':
+ resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.12':
+ resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.12':
+ resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.12':
+ resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.12':
+ resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.12':
+ resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.12':
+ resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.12':
+ resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.12':
+ resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.8.1':
+ resolution: {integrity: sha512-vafrdi15JHBFh1qWOAucyJwlEEuhdoMof28jOdKdgnwZ7bXwd6v4wzHYSmAVLwnx0yc7bqAtfJ0EZQV1WAzV/Q==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
+
+ '@eslint-community/eslint-utils@4.10.1':
+ resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/eslint-utils@4.9.0':
+ resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/compat@2.1.1':
+ resolution: {integrity: sha512-rMcy8GSrwNzcISX/BlTDY/GLB4eCopEuy9woIls3To+15OLxykZrxxq+WUcylCPCQ6F4MujjBM1DX5V1aqI3Vw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: ^8.40 || 9 || 10
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+
+ '@eslint/config-array@0.21.1':
+ resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.4.2':
+ resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.5.5':
+ resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/core@0.17.0':
+ resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@1.2.1':
+ resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/eslintrc@3.3.1':
+ resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.39.1':
+ resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/markdown@7.5.1':
+ resolution: {integrity: sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.7':
+ resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.4.1':
+ resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.7.3':
+ resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.7':
+ resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@iconify-json/icon-park-outline@1.2.4':
+ resolution: {integrity: sha512-NyZxXe2gD2TbTOyoRRMdtEJhr6i2KQCdDlYYoOn5oZLndQjwpIhw79hzeFhXvP38/o40D3gQ+l+IaSJgbB+0TQ==}
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@3.1.7':
+ resolution: {integrity: sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==}
+
+ '@iconify/vue@5.0.3':
+ resolution: {integrity: sha512-e9x6QZe2/HyeHUog0jzZJSZts7m/YntPYp/p4OqtZEXpHznxWnQpcX30YpuaUH43h69l3YeR1FCJ8V1t3qd7qA==}
+ peerDependencies:
+ vue: '>=3.0.0'
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@keyv/bigmap@1.1.0':
+ resolution: {integrity: sha512-MX7XIUNwVRK+hjZcAbNJ0Z8DREo+Weu9vinBOjGU1thEi9F6vPhICzBbk4CCf3eEefKRz7n6TfZXwUFZTSgj8Q==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ keyv: ^5.5.3
+
+ '@keyv/serialize@1.1.1':
+ resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@ota-meshi/ast-token-store@0.3.0':
+ resolution: {integrity: sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+
+ '@pkgr/core@0.3.6':
+ resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+ '@rolldown/pluginutils@1.0.0-beta.29':
+ resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
+
+ '@rolldown/pluginutils@1.0.0-beta.47':
+ resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==}
+
+ '@rollup/rollup-android-arm-eabi@4.52.5':
+ resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.52.5':
+ resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.52.5':
+ resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.52.5':
+ resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.52.5':
+ resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.52.5':
+ resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
+ resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.5':
+ resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.5':
+ resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.52.5':
+ resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.5':
+ resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.5':
+ resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.5':
+ resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.5':
+ resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.5':
+ resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.52.5':
+ resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.52.5':
+ resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-openharmony-arm64@4.52.5':
+ resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.5':
+ resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.5':
+ resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.52.5':
+ resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.52.5':
+ resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sindresorhus/base62@1.0.0':
+ resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==}
+ engines: {node: '>=18'}
+
+ '@stylistic/eslint-plugin@5.10.0':
+ resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^9.0.0 || ^10.0.0
+
+ '@types/debug@4.1.12':
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/lodash-es@4.17.12':
+ resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+ '@types/lodash@4.17.20':
+ resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@24.10.0':
+ resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
+
+ '@types/node@26.6.3':
+ resolution: {integrity: sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==}
+
+ '@types/sortablejs@1.15.9':
+ resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@typescript-eslint/eslint-plugin@8.70.1':
+ resolution: {integrity: sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.70.1
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/parser@8.70.1':
+ resolution: {integrity: sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/project-service@8.70.1':
+ resolution: {integrity: sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/scope-manager@8.70.1':
+ resolution: {integrity: sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.70.1':
+ resolution: {integrity: sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/type-utils@8.70.1':
+ resolution: {integrity: sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/types@8.70.1':
+ resolution: {integrity: sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.70.1':
+ resolution: {integrity: sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/utils@8.70.1':
+ resolution: {integrity: sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.70.1':
+ resolution: {integrity: sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@vitejs/plugin-vue-jsx@5.1.1':
+ resolution: {integrity: sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+ vue: ^3.0.0
+
+ '@vitejs/plugin-vue@6.0.1':
+ resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+ vue: ^3.2.25
+
+ '@vitest/eslint-plugin@1.6.27':
+ resolution: {integrity: sha512-X1RCAfwbatG4GFbJ/1PIHP9MSZMt0JJThqbYID+vS4L783k6QGlLPe421wQE8dHXuGCcenVLsydiM4qzsYWxhg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@typescript-eslint/eslint-plugin': '*'
+ eslint: '>=8.57.0'
+ typescript: '>=5.0.0'
+ vitest: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/eslint-plugin':
+ optional: true
+ typescript:
+ optional: true
+ vitest:
+ optional: true
+
+ '@volar/language-core@2.4.23':
+ resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
+
+ '@volar/source-map@2.4.23':
+ resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
+
+ '@volar/typescript@2.4.23':
+ resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
+
+ '@vue/babel-helper-vue-transform-on@1.5.0':
+ resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==}
+
+ '@vue/babel-plugin-jsx@1.5.0':
+ resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+
+ '@vue/babel-plugin-resolve-type@1.5.0':
+ resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@vue/compiler-core@3.5.23':
+ resolution: {integrity: sha512-nW7THWj5HOp085ROk65LwaoxuzDsjIxr485F4iu63BoxsXoSqKqmsUUoP4A7Gl67DgIgi0zJ8JFgHfvny/74MA==}
+
+ '@vue/compiler-dom@3.5.23':
+ resolution: {integrity: sha512-AT8RMw0vEzzzO0JU5gY0F6iCzaWUIh/aaRVordzMBKXRpoTllTT4kocHDssByPsvodNCfump/Lkdow2mT/O5KQ==}
+
+ '@vue/compiler-sfc@3.5.23':
+ resolution: {integrity: sha512-3QTEUo4qg7FtQwaDJa8ou1CUikx5WTtZlY61rRRDu3lK2ZKrGoAGG8mvDgOpDsQ4A1bez9s+WtBB6DS2KuFCPw==}
+
+ '@vue/compiler-ssr@3.5.23':
+ resolution: {integrity: sha512-Hld2xphbMjXs9Q9WKxPf2EqmE+Rq/FEDnK/wUBtmYq74HCV4XDdSCheAaB823OQXIIFGq9ig/RbAZkF9s4U0Ow==}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/devtools-api@7.7.7':
+ resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
+
+ '@vue/devtools-core@8.0.5':
+ resolution: {integrity: sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==}
+ peerDependencies:
+ vue: ^3.0.0
+
+ '@vue/devtools-kit@7.7.7':
+ resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+
+ '@vue/devtools-kit@8.0.5':
+ resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==}
+
+ '@vue/devtools-shared@7.7.7':
+ resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+
+ '@vue/devtools-shared@8.0.5':
+ resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==}
+
+ '@vue/language-core@3.1.3':
+ resolution: {integrity: sha512-KpR1F/eGAG9D1RZ0/T6zWJs6dh/pRLfY5WupecyYKJ1fjVmDMgTPw9wXmKv2rBjo4zCJiOSiyB8BDP1OUwpMEA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/reactivity@3.5.23':
+ resolution: {integrity: sha512-ji5w0qvrPyBmBx5Ldv4QGNsw0phgRreEvjt0iUf1lei2Sm8//9ZAi78uM2ZjsT5gk0YZilLuoRCIMvtuZlHMJw==}
+
+ '@vue/runtime-core@3.5.23':
+ resolution: {integrity: sha512-LMB0S6/G7mFJcpQeQaZrbsthFbWrIX8FVTzu5x9U3Ec8YW5MY1CGAnBBHNj+TPOBu3pIbtPpjrXtcaN04X+aBw==}
+
+ '@vue/runtime-dom@3.5.23':
+ resolution: {integrity: sha512-r/PYc8W9THzEL0UExpTkV+d31zO+Jid/RMZIDG6aS/NekOEUHuCJkJgftySWZw7JTJO/+q9Kxkg8p+i7Q7Q+ew==}
+
+ '@vue/server-renderer@3.5.23':
+ resolution: {integrity: sha512-NiWZsNCsXA20/VufcrW5u+Trt/PyFlpMmxaB2KERYM8eZgUoKUjXxJQb9ypq+LZ0Sp3XHJGNBR8DkhRnkKAMUw==}
+ peerDependencies:
+ vue: 3.5.23
+
+ '@vue/shared@3.5.23':
+ resolution: {integrity: sha512-0YZ1DYuC5o/YJPf6pFdt2KYxVGDxkDbH/1NYJnVJWUkzr8ituBEmFVQRNX2gCaAsFEjEDnLkWpgqlZA7htgS/g==}
+
+ '@vue/tsconfig@0.7.0':
+ resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==}
+ peerDependencies:
+ typescript: 5.x
+ vue: ^3.4.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ vue:
+ optional: true
+
+ '@vueuse/core@13.9.0':
+ resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/metadata@13.9.0':
+ resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
+
+ '@vueuse/shared@13.9.0':
+ resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ acorn@8.18.0:
+ resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ajv@8.17.1:
+ resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
+
+ alien-signals@3.0.6:
+ resolution: {integrity: sha512-gCs0YqC1mkYGC6IRXsSrA62ShOSv1FlVN5tRp/Cs2vRWLK/BAeluWIdfsl253pFQPznKEvRmHhfep7crWfyfWQ==}
+
+ animate.css@4.1.1:
+ resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansis@4.2.0:
+ resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
+ engines: {node: '>=14'}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ are-docs-informative@0.0.2:
+ resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
+ engines: {node: '>=14'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ axios@0.27.2:
+ resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
+
+ b-tween@0.3.3:
+ resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==}
+
+ b-validate@1.5.3:
+ resolution: {integrity: sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ balanced-match@2.0.0:
+ resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ baseline-browser-mapping@2.8.25:
+ resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==}
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ birpc@2.7.0:
+ resolution: {integrity: sha512-tub/wFGH49vNCm0xraykcY3TcRgX/3JsALYq/Lwrtti+bTyFHkCUAWF5wgYoie8P41wYwig2mIKiqoocr1EkEQ==}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@5.0.12:
+ resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==}
+ engines: {node: 20 || >=22}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.27.0:
+ resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ builtin-modules@5.0.0:
+ resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==}
+ engines: {node: '>=18.20'}
+
+ bundle-name@4.1.0:
+ resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+ engines: {node: '>=18'}
+
+ cac@7.0.0:
+ resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==}
+ engines: {node: '>=20.19.0'}
+
+ cacheable@2.1.1:
+ resolution: {integrity: sha512-LmF4AXiSNdiRbI2UjH8pAp9NIXxeQsTotpEaegPiDcnN0YPygDJDV3l/Urc0mL72JWdATEorKqIHEx55nDlONg==}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001754:
+ resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ change-case@5.4.4:
+ resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ ci-info@4.3.1:
+ resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==}
+ engines: {node: '>=8'}
+
+ clean-regexp@1.0.0:
+ resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
+ engines: {node: '>=4'}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@3.2.1:
+ resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
+
+ colord@2.9.3:
+ resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ comment-parser@1.4.1:
+ resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
+ engines: {node: '>= 12.0.0'}
+
+ comment-parser@1.4.6:
+ resolution: {integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==}
+ engines: {node: '>= 12.0.0'}
+
+ comment-parser@1.4.7:
+ resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==}
+ engines: {node: '>= 12.0.0'}
+
+ compute-scroll-into-view@1.0.20:
+ resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ confbox@0.1.8:
+ resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+ confbox@0.2.2:
+ resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ copy-anything@2.0.6:
+ resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
+
+ copy-anything@4.0.5:
+ resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
+ engines: {node: '>=18'}
+
+ core-js-compat@3.46.0:
+ resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==}
+
+ cosmiconfig@9.0.0:
+ resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-functions-list@3.2.3:
+ resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==}
+ engines: {node: '>=12 || >=16'}
+
+ css-tree@3.1.0:
+ resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ dayjs@1.11.19:
+ resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.2.0:
+ resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ default-browser-id@5.0.1:
+ resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
+ engines: {node: '>=18'}
+
+ default-browser@5.4.0:
+ resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==}
+ engines: {node: '>=18'}
+
+ define-lazy-prop@3.0.0:
+ resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+ engines: {node: '>=12'}
+
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ echarts@6.1.0:
+ resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==}
+
+ electron-to-chromium@1.5.245:
+ resolution: {integrity: sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ empathic@2.0.0:
+ resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
+ engines: {node: '>=14'}
+
+ empathic@2.1.0:
+ resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==}
+ engines: {node: '>=14'}
+
+ enhanced-resolve@5.18.3:
+ resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ errno@0.1.8:
+ resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+ hasBin: true
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ error-stack-parser-es@1.0.5:
+ resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.25.12:
+ resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ eslint-compat-utils@0.5.1:
+ resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-config-flat-gitignore@2.4.0:
+ resolution: {integrity: sha512-JhYC+V7qEDiCDsbJcVP8fxNc62fk4+i91YLP/YvmVHlkaQ8Xo99olYN8MO5COdxl7onGlsrfZVONe5NYsZP1UA==}
+ peerDependencies:
+ eslint: ^9.5.0 || ^10.0.0
+
+ eslint-flat-config-utils@3.2.0:
+ resolution: {integrity: sha512-PHgo1X5uqIorJONLVD9BIaOSdoYFD3z/AeJljdqDPlWVRpeCYkDbK9k0AXoYVqqNJr6FEYIEr5Rm2TSktLQcHw==}
+
+ eslint-json-compat-utils@0.2.3:
+ resolution: {integrity: sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@eslint/json': '*'
+ eslint: '*'
+ jsonc-eslint-parser: ^2.4.0 || ^3.0.0
+ peerDependenciesMeta:
+ '@eslint/json':
+ optional: true
+
+ eslint-merge-processors@2.0.0:
+ resolution: {integrity: sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-antfu@3.2.3:
+ resolution: {integrity: sha512-U2fnz/H0gFPxpuC7QpaHa0Jv2AgCZ5hunp36SOP/yWo8yFzgvMh8X4pZ4uN4IKoqtBhk7G3HuVa93Urf51+sZg==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-command@3.5.3:
+ resolution: {integrity: sha512-JIhmUOW2K2Dw1K+p4mtj5potkqTZ4ZaicrTkgCygPZCWErP2+O2F46n7ZwGKapeEjiwcVqoY8u01SxNUUXWPeQ==}
+ peerDependencies:
+ '@typescript-eslint/typescript-estree': '*'
+ '@typescript-eslint/utils': '*'
+ eslint: '*'
+
+ eslint-plugin-depend@1.5.0:
+ resolution: {integrity: sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A==}
+ peerDependencies:
+ eslint: '>=8.40.0'
+
+ eslint-plugin-es-x@7.8.0:
+ resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=8'
+
+ eslint-plugin-import-lite@0.5.2:
+ resolution: {integrity: sha512-XvfdWOC5dSLEI9krIPRlNmKSI2ViIE9pVylzfV9fCq0ZpDaNeUk6o0wZv0OzN83QdadgXp1NsY0qjLINxwYCsw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=9.0.0'
+
+ eslint-plugin-jsdoc@62.9.0:
+ resolution: {integrity: sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
+
+ eslint-plugin-jsonc@3.4.2:
+ resolution: {integrity: sha512-q5xzjCYFQuhFomTbctXzd3STfDJ8XduvaigjQmMEbP2gzSKrc58y3Fv6D775/cIK1/sRUn+4kpljiU2iW8k0zw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: '>=9.38.0'
+
+ eslint-plugin-n@17.24.0:
+ resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=8.23.0'
+
+ eslint-plugin-no-only-tests@3.3.0:
+ resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
+ engines: {node: '>=5.0.0'}
+
+ eslint-plugin-perfectionist@5.12.1:
+ resolution: {integrity: sha512-krdSE7Dz+TCtcVr2+rfNhlOIFFBJcUdoRvjaN7JQnsaVnxJw+BZh9wMkY3MSLCJat+yW+UPTQLKS82Epc7MGZA==}
+ engines: {node: ^20.0.0 || >=22.0.0}
+ peerDependencies:
+ eslint: ^8.45.0 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+
+ eslint-plugin-pnpm@1.9.1:
+ resolution: {integrity: sha512-8++1Egww2cqc9Wylmt7P+xXNMQVCosAZ4m4W4Gc0V5lGea8HFByHTRvUI+ZvvpO8fLXGg2O4MQc90EhuI3mMdQ==}
+ peerDependencies:
+ eslint: ^9.0.0 || ^10.0.0
+
+ eslint-plugin-regexp@3.3.1:
+ resolution: {integrity: sha512-58bGUci/AaWAo1ETj6Lsnmy6QXwnSyVONRA805828nnqp7vrTwdaQMc02vtowmYB3A5OB3SLr3r5HgmfxX4dLg==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: '>=9.38.0'
+
+ eslint-plugin-toml@1.5.0:
+ resolution: {integrity: sha512-qBjRywEkKxO2uYOjus//6GVF1r+Hg5QDkRO8RTY6XcaXgWfBU0DhwpFmJa2Ljf0Sz49r7DdZlpKwwHmJ4nmH1Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: '>=9.38.0'
+
+ eslint-plugin-unicorn@63.0.0:
+ resolution: {integrity: sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA==}
+ engines: {node: ^20.10.0 || >=21.0.0}
+ peerDependencies:
+ eslint: '>=9.38.0'
+
+ eslint-plugin-unused-imports@4.4.1:
+ resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==}
+ peerDependencies:
+ '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
+ eslint: ^10.0.0 || ^9.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@typescript-eslint/eslint-plugin':
+ optional: true
+
+ eslint-plugin-vue@10.11.1:
+ resolution: {integrity: sha512-pasVXhl7wUmIvhujoqs/m0mJ9r59QW1IR9XXYKGR0FETzSM+Hhlf4qKIYgWiDDP+9YKv4YfbwskD7LqIdVcgQw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
+ '@typescript-eslint/parser': ^7.0.0 || ^8.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ vue-eslint-parser: ^10.3.0
+ peerDependenciesMeta:
+ '@stylistic/eslint-plugin':
+ optional: true
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-yml@3.8.1:
+ resolution: {integrity: sha512-E/70psRwxz5EJ8dBtzrFfqSiiInuSytbdtFCjueb/GcKjsWzPBfxfhtQePImMAPjHwMA/VDurO7DJwStDJ4wWg==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
+ peerDependencies:
+ eslint: '>=9.38.0'
+
+ eslint-processor-vue-blocks@2.0.0:
+ resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==}
+ peerDependencies:
+ '@vue/compiler-sfc': ^3.3.0
+ eslint: '>=9.0.0'
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@9.39.1:
+ resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ espree@11.2.0:
+ resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ exsolve@1.0.7:
+ resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-string-truncated-width@3.0.3:
+ resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
+
+ fast-string-width@3.0.2:
+ resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
+
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+
+ fast-wrap-ansi@0.2.2:
+ resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==}
+
+ fastest-levenshtein@1.0.16:
+ resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==}
+ engines: {node: '>= 4.9.1'}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fault@2.0.1:
+ resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@10.1.4:
+ resolution: {integrity: sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==}
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up-simple@1.0.1:
+ resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
+ engines: {node: '>=18'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flat-cache@6.1.18:
+ resolution: {integrity: sha512-JUPnFgHMuAVmLmoH9/zoZ6RHOt5n9NlUw/sDXsTbROJ2SFoS2DS4s+swAV6UTeTbGH/CAsZIE6M8TaG/3jVxgQ==}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ follow-redirects@1.15.11:
+ resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ form-data@4.0.4:
+ resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
+ engines: {node: '>= 6'}
+
+ format@0.2.2:
+ resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
+ engines: {node: '>=0.4.x'}
+
+ fs-extra@10.1.0:
+ resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+ engines: {node: '>=12'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.13.0:
+ resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
+
+ github-slugger@2.0.0:
+ resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ global-modules@2.0.0:
+ resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==}
+ engines: {node: '>=6'}
+
+ global-prefix@3.0.0:
+ resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
+ engines: {node: '>=6'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@15.15.0:
+ resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
+ engines: {node: '>=18'}
+
+ globals@16.5.0:
+ resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
+ engines: {node: '>=18'}
+
+ globals@17.12.0:
+ resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==}
+ engines: {node: '>=18'}
+
+ globby@11.1.0:
+ resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+ engines: {node: '>=10'}
+
+ globjoin@0.1.4:
+ resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==}
+
+ globrex@0.1.2:
+ resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ hookified@1.12.2:
+ resolution: {integrity: sha512-aokUX1VdTpI0DUsndvW+OiwmBpKCu/NgRsSSkuSY0zq8PY6Q6a+lmOfAFDXAAOtBqJELvcWY9L1EVtzjbQcMdg==}
+
+ html-entities@2.6.0:
+ resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
+
+ html-tags@3.3.1:
+ resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+ engines: {node: '>=8'}
+
+ htmlparser2@8.0.2:
+ resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ image-size@0.5.5:
+ resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+
+ immutable@5.1.4:
+ resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ import-meta-resolve@4.2.0:
+ resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@5.0.0:
+ resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+ engines: {node: '>=12'}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-arrayish@0.3.4:
+ resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-builtin-module@5.0.0:
+ resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==}
+ engines: {node: '>=18.20'}
+
+ is-docker@3.0.0:
+ resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-inside-container@1.0.0:
+ resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+ engines: {node: '>=14.16'}
+ hasBin: true
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-plain-object@5.0.0:
+ resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+ engines: {node: '>=0.10.0'}
+
+ is-what@3.14.1:
+ resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
+
+ is-what@5.5.0:
+ resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
+ engines: {node: '>=18'}
+
+ is-wsl@3.1.0:
+ resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+ engines: {node: '>=16'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsdoc-type-pratt-parser@7.2.0:
+ resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==}
+ engines: {node: '>=20.0.0'}
+
+ jsdoc-type-pratt-parser@9.2.2:
+ resolution: {integrity: sha512-eFY7Rlscvv/BpY6UsVsjKwMhu1tJO/F4U/vC9g3yeRy/JqszuO1UP6Vdq+85G+3FP6fEw/sWR8a5ftPFY86csw==}
+ engines: {node: ^22.22.2 || >=24.15.0}
+
+ jsencrypt@3.5.4:
+ resolution: {integrity: sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonc-eslint-parser@3.3.0:
+ resolution: {integrity: sha512-hYTGkHGNRZnXOFZ1urhINADoqDrGfpy53cjw+dxk84QE0pUDujQzeUeamNs6Mz44/TKD49z2x6/GVSu4ZrtA+Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ keyv@5.5.3:
+ resolution: {integrity: sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==}
+
+ kind-of@6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+
+ known-css-properties@0.36.0:
+ resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==}
+
+ known-css-properties@0.37.0:
+ resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==}
+
+ kolorist@1.8.0:
+ resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+ less@4.4.2:
+ resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ local-pkg@1.1.2:
+ resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
+ engines: {node: '>=14'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.truncate@4.4.2:
+ resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ make-dir@2.1.0:
+ resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+ engines: {node: '>=6'}
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ mathml-tag-names@2.1.3:
+ resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.2:
+ resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
+
+ mdast-util-frontmatter@2.0.1:
+ resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-markdown@2.1.2:
+ resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ mdn-data@2.12.2:
+ resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+ mdn-data@2.25.0:
+ resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==}
+
+ meow@13.2.0:
+ resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
+ engines: {node: '>=18'}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-extension-frontmatter@2.0.0:
+ resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ minimatch@10.2.6:
+ resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ mlly@1.8.0:
+ resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
+
+ module-replacements@2.11.0:
+ resolution: {integrity: sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ nanoid@5.1.6:
+ resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ natural-orderby@5.0.0:
+ resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==}
+ engines: {node: '>=18'}
+
+ needle@3.3.1:
+ resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
+ engines: {node: '>= 4.4.x'}
+ hasBin: true
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ nprogress@0.2.0:
+ resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ number-precision@1.6.0:
+ resolution: {integrity: sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==}
+
+ object-deep-merge@2.0.1:
+ resolution: {integrity: sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==}
+
+ ohash@2.0.11:
+ resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+ open@10.2.0:
+ resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
+ engines: {node: '>=18'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ package-manager-detector@1.5.0:
+ resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==}
+
+ package-manager-detector@1.8.0:
+ resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-gitignore@2.0.0:
+ resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==}
+ engines: {node: '>=14'}
+
+ parse-imports-exports@0.2.4:
+ resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ parse-node-version@1.0.1:
+ resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
+ engines: {node: '>= 0.10'}
+
+ parse-statements@1.0.11:
+ resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+ perfect-debounce@2.0.0:
+ resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ picomatch@4.0.7:
+ resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
+ engines: {node: '>=12'}
+
+ pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+
+ pinia-plugin-persistedstate@4.7.1:
+ resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==}
+ peerDependencies:
+ '@nuxt/kit': '>=3.0.0'
+ '@pinia/nuxt': '>=0.10.0'
+ pinia: '>=3.0.0'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@pinia/nuxt':
+ optional: true
+ pinia:
+ optional: true
+
+ pinia@3.0.4:
+ resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
+ peerDependencies:
+ typescript: '>=4.5.0'
+ vue: ^3.5.11
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ pkg-types@1.3.1:
+ resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+ pkg-types@2.3.0:
+ resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+
+ pluralize@8.0.0:
+ resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+ engines: {node: '>=4'}
+
+ pnpm-workspace-yaml@1.9.1:
+ resolution: {integrity: sha512-Xj/T2X6DNAWbtk/9UQ0/TJRswltiHZevmcMjqoL7WrpJi67lu3qaslU4+I+zJ8wtwqmuvAE0KkB8rbv2ZdogBg==}
+
+ postcss-html@1.8.0:
+ resolution: {integrity: sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==}
+ engines: {node: ^12 || >=14}
+
+ postcss-media-query-parser@0.2.3:
+ resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==}
+
+ postcss-resolve-nested-selector@0.1.6:
+ resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==}
+
+ postcss-safe-parser@6.0.0:
+ resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.3.3
+
+ postcss-safe-parser@7.0.1:
+ resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==}
+ engines: {node: '>=18.0'}
+ peerDependencies:
+ postcss: ^8.4.31
+
+ postcss-scss@4.0.9:
+ resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.4.29
+
+ postcss-selector-parser@7.1.0:
+ resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@7.1.6:
+ resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==}
+ engines: {node: '>=4'}
+
+ postcss-sorting@9.1.0:
+ resolution: {integrity: sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==}
+ peerDependencies:
+ postcss: ^8.4.20
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prr@1.0.1:
+ resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qified@0.5.1:
+ resolution: {integrity: sha512-+BtFN3dCP+IaFA6IYNOu/f/uK1B8xD2QWyOeCse0rjtAebBmkzgd2d1OAXi3ikAzJMIBSdzZDNZ3wZKEUDQs5w==}
+ engines: {node: '>=20'}
+
+ quansync@0.2.11:
+ resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ refa@0.12.1:
+ resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ regexp-ast-analysis@0.7.1:
+ resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ regexp-tree@0.1.27:
+ resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
+ hasBin: true
+
+ regjsparser@0.13.3:
+ resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==}
+ hasBin: true
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ reserved-identifiers@1.2.0:
+ resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==}
+ engines: {node: '>=18'}
+
+ resize-observer-polyfill@1.5.1:
+ resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.52.5:
+ resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-applescript@7.1.0:
+ resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
+ engines: {node: '>=18'}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sass@1.93.3:
+ resolution: {integrity: sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ sax@1.4.3:
+ resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
+
+ scroll-into-view-if-needed@2.2.31:
+ resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
+
+ scslre@0.3.0:
+ resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
+ engines: {node: ^14.0.0 || >=16.0.0}
+
+ scule@1.3.0:
+ resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
+ semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ semver@7.8.5:
+ resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ simple-swizzle@0.2.4:
+ resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
+
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ slice-ansi@4.0.0:
+ resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+ engines: {node: '>=10'}
+
+ sortablejs@1.15.6:
+ resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ spdx-exceptions@2.5.0:
+ resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
+
+ spdx-expression-parse@4.0.0:
+ resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==}
+
+ spdx-license-ids@3.0.22:
+ resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==}
+
+ speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-indent@4.1.1:
+ resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
+ engines: {node: '>=12'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ strip-literal@3.1.0:
+ resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
+
+ stylelint-config-html@1.1.0:
+ resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==}
+ engines: {node: ^12 || >=14}
+ peerDependencies:
+ postcss-html: ^1.0.0
+ stylelint: '>=14.0.0'
+
+ stylelint-config-recess-order@7.4.0:
+ resolution: {integrity: sha512-W3G517cBaMDYRX5Fzhro4fhRkkLafLgVSPfQnhxbiLyMnbLq47RMF/NRaOJ4OQsKWYhsIHOIc2Q3VL0X3Q7oPg==}
+ peerDependencies:
+ stylelint: '>=16.18'
+ stylelint-order: '>=7'
+
+ stylelint-config-recommended-scss@16.0.2:
+ resolution: {integrity: sha512-aUTHhPPWCvFyWaxtckJlCPaXTDFsp4pKO8evXNCsW9OwsaUWyMd6jvcUhSmfGWPrTddvzNqK4rS/UuSLcbVGdQ==}
+ engines: {node: '>=20'}
+ peerDependencies:
+ postcss: ^8.3.3
+ stylelint: ^16.24.0
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+
+ stylelint-config-recommended-vue@1.6.1:
+ resolution: {integrity: sha512-lLW7hTIMBiTfjenGuDq2kyHA6fBWd/+Df7MO4/AWOxiFeXP9clbpKgg27kHfwA3H7UNMGC7aeP3mNlZB5LMmEQ==}
+ engines: {node: ^12 || >=14}
+ peerDependencies:
+ postcss-html: ^1.0.0
+ stylelint: '>=14.0.0'
+
+ stylelint-config-recommended@17.0.0:
+ resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ stylelint: ^16.23.0
+
+ stylelint-config-standard-scss@16.0.0:
+ resolution: {integrity: sha512-/FHECLUu+med/e6OaPFpprG86ShC4SYT7Tzb2PTVdDjJsehhFBOioSlWqYFqJxmGPIwO3AMBxNo+kY3dxrbczA==}
+ engines: {node: '>=20'}
+ peerDependencies:
+ postcss: ^8.3.3
+ stylelint: ^16.23.1
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+
+ stylelint-config-standard@39.0.1:
+ resolution: {integrity: sha512-b7Fja59EYHRNOTa3aXiuWnhUWXFU2Nfg6h61bLfAb5GS5fX3LMUD0U5t4S8N/4tpHQg3Acs2UVPR9jy2l1g/3A==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ stylelint: ^16.23.0
+
+ stylelint-order@7.0.0:
+ resolution: {integrity: sha512-rSWxx0KscYfxU02wEskKXES9lkRzuuONMMNkZ7SUc6uiF3tDKm7e+sE0Ax/SBlG4TUf1sp1R6f3/SlsPGmzthg==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ stylelint: ^16.18.0
+
+ stylelint-scss@6.12.1:
+ resolution: {integrity: sha512-UJUfBFIvXfly8WKIgmqfmkGKPilKB4L5j38JfsDd+OCg2GBdU0vGUV08Uw82tsRZzd4TbsUURVVNGeOhJVF7pA==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ stylelint: ^16.0.2
+
+ stylelint@16.25.0:
+ resolution: {integrity: sha512-Li0avYWV4nfv1zPbdnxLYBGq4z8DVZxbRgx4Kn6V+Uftz1rMoF1qiEI3oL4kgWqyYgCgs7gT5maHNZ82Gk03vQ==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+
+ superjson@2.2.5:
+ resolution: {integrity: sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==}
+ engines: {node: '>=16'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-hyperlinks@3.2.0:
+ resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==}
+ engines: {node: '>=14.18'}
+
+ svg-tags@1.0.0:
+ resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+
+ synckit@0.11.13:
+ resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ table@6.9.0:
+ resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
+ engines: {node: '>=10.0.0'}
+
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ engines: {node: '>=6'}
+
+ tinyexec@1.0.2:
+ resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
+ engines: {node: '>=18'}
+
+ tinyexec@1.3.1:
+ resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ to-valid-identifier@1.0.0:
+ resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==}
+ engines: {node: '>=20'}
+
+ toml-eslint-parser@1.0.3:
+ resolution: {integrity: sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ ts-api-utils@2.5.0:
+ resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ ts-declaration-location@1.0.7:
+ resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==}
+ peerDependencies:
+ typescript: '>=4.0.0'
+
+ tslib@2.3.0:
+ resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ufo@1.6.1:
+ resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+
+ undici-types@7.16.0:
+ resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
+
+ undici-types@8.9.0:
+ resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==}
+
+ unimport@5.5.0:
+ resolution: {integrity: sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==}
+ engines: {node: '>=18.12.0'}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unplugin-auto-import@20.2.0:
+ resolution: {integrity: sha512-vfBI/SvD9hJqYNinipVOAj5n8dS8DJXFlCKFR5iLDp2SaQwsfdnfLXgZ+34Kd3YY3YEY9omk8XQg0bwos3Q8ug==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': ^4.0.0
+ '@vueuse/core': '*'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@vueuse/core':
+ optional: true
+
+ unplugin-icons@22.5.0:
+ resolution: {integrity: sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==}
+ peerDependencies:
+ '@svgr/core': '>=7.0.0'
+ '@svgx/core': ^1.0.1
+ '@vue/compiler-sfc': ^3.0.2 || ^2.7.0
+ svelte: ^3.0.0 || ^4.0.0 || ^5.0.0
+ vue-template-compiler: ^2.6.12
+ vue-template-es2015-compiler: ^1.9.0
+ peerDependenciesMeta:
+ '@svgr/core':
+ optional: true
+ '@svgx/core':
+ optional: true
+ '@vue/compiler-sfc':
+ optional: true
+ svelte:
+ optional: true
+ vue-template-compiler:
+ optional: true
+ vue-template-es2015-compiler:
+ optional: true
+
+ unplugin-utils@0.3.1:
+ resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==}
+ engines: {node: '>=20.19.0'}
+
+ unplugin-vue-components@29.2.0:
+ resolution: {integrity: sha512-QxBeBdmEflgtJRgMQMc/z/JVV5lcwXN5nOy5ehX6CKDGylIu6Qn4Goy8X95S0qOxF7EdI+uNhdBd4v5i0bvzCw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/parser': ^7.15.8
+ '@nuxt/kit': ^3.2.2 || ^4.0.0
+ vue: 2 || 3
+ peerDependenciesMeta:
+ '@babel/parser':
+ optional: true
+ '@nuxt/kit':
+ optional: true
+
+ unplugin@2.3.10:
+ resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==}
+ engines: {node: '>=18.12.0'}
+
+ update-browserslist-db@1.1.4:
+ resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ verkit@0.3.2:
+ resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==}
+ engines: {node: '>=18.12.0'}
+
+ vite-dev-rpc@1.1.0:
+ resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0
+
+ vite-hot-client@2.1.0:
+ resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
+ peerDependencies:
+ vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite-plugin-compression@0.5.1:
+ resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
+ peerDependencies:
+ vite: '>=2.0.0'
+
+ vite-plugin-inspect@11.3.3:
+ resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': '*'
+ vite: ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+
+ vite-plugin-vue-devtools@8.0.5:
+ resolution: {integrity: sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==}
+ engines: {node: '>=v14.21.3'}
+ peerDependencies:
+ vite: ^6.0.0 || ^7.0.0-0
+
+ vite-plugin-vue-inspector@5.3.2:
+ resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==}
+ peerDependencies:
+ vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite@7.2.1:
+ resolution: {integrity: sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vscode-uri@3.1.0:
+ resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+ vue-color-kit@1.0.6:
+ resolution: {integrity: sha512-56oSUp8hGIZ3E4ayZLqTDAb2C7VzaWAC1EVUsZEvPCD2wiiiFd2L2BiEGN1ingfz538sYWKNXxMZQGslqbMiBg==}
+ peerDependencies:
+ vue: ^3.0.5
+
+ vue-draggable-plus@0.6.0:
+ resolution: {integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==}
+ peerDependencies:
+ '@types/sortablejs': ^1.15.0
+ '@vue/composition-api': '*'
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-eslint-parser@10.4.1:
+ resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+
+ vue-router@4.6.3:
+ resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ vue-tsc@3.1.3:
+ resolution: {integrity: sha512-StMNfZHwPIXQgY3KxPKM0Jsoc8b46mDV3Fn2UlHCBIwRJApjqrSwqeMYgWf0zpN+g857y74pv7GWuBm+UqQe1w==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.23:
+ resolution: {integrity: sha512-CfvZv/vI52xUhumUvHtD6iFIS78nGWfX4IJnHfBGhpqMI0CwDq2YEngXOeaBFMRmiArcqczuVrLxurvesTYT9w==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ webpack-virtual-modules@0.6.2:
+ resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+ which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ write-file-atomic@5.0.1:
+ resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ wsl-utils@0.1.0:
+ resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
+ engines: {node: '>=18'}
+
+ xe-utils@3.7.9:
+ resolution: {integrity: sha512-LWH6M7g+TKX8P2fqGxDPgJQygiULhbMtpmfxTMxJTUjuUv9y2+I45UCcVLh5AnmJSxDV1xTxsq7G5P9Eid06JQ==}
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yaml-eslint-parser@2.1.0:
+ resolution: {integrity: sha512-1zo9KRfp6vIAXBEhWAz09ex3hUwh3T+/6dGbGteHvNseA0Uk4FgQnPES55klZ6cDzSy9FpgKS0D/CoLUvCCj4w==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yaml@2.9.1:
+ resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zrender@6.1.0:
+ resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@antfu/eslint-config@7.7.3(@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3))(@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3))(@vue/compiler-sfc@3.5.23)(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@clack/prompts': 1.8.1
+ '@e18e/eslint-plugin': 0.2.0(eslint@9.39.1)
+ '@eslint-community/eslint-plugin-eslint-comments': 4.8.1(eslint@9.39.1)
+ '@eslint/markdown': 7.5.1
+ '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.1)
+ '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ '@vitest/eslint-plugin': 1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ ansis: 4.2.0
+ cac: 7.0.0
+ eslint: 9.39.1
+ eslint-config-flat-gitignore: 2.4.0(eslint@9.39.1)
+ eslint-flat-config-utils: 3.2.0
+ eslint-merge-processors: 2.0.0(eslint@9.39.1)
+ eslint-plugin-antfu: 3.2.3(eslint@9.39.1)
+ eslint-plugin-command: 3.5.3(@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3))(@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ eslint-plugin-import-lite: 0.5.2(eslint@9.39.1)
+ eslint-plugin-jsdoc: 62.9.0(eslint@9.39.1)
+ eslint-plugin-jsonc: 3.4.2(eslint@9.39.1)
+ eslint-plugin-n: 17.24.0(eslint@9.39.1)(typescript@5.9.3)
+ eslint-plugin-no-only-tests: 3.3.0
+ eslint-plugin-perfectionist: 5.12.1(eslint@9.39.1)(typescript@5.9.3)
+ eslint-plugin-pnpm: 1.9.1(eslint@9.39.1)
+ eslint-plugin-regexp: 3.3.1(eslint@9.39.1)
+ eslint-plugin-toml: 1.5.0(eslint@9.39.1)
+ eslint-plugin-unicorn: 63.0.0(eslint@9.39.1)
+ eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ eslint-plugin-vue: 10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@9.39.1))(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.4.1(eslint@9.39.1))
+ eslint-plugin-yml: 3.8.1(eslint@9.39.1)
+ eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.23)(eslint@9.39.1)
+ globals: 17.12.0
+ local-pkg: 1.1.2
+ parse-gitignore: 2.0.0
+ toml-eslint-parser: 1.0.3
+ vue-eslint-parser: 10.4.1(eslint@9.39.1)
+ yaml-eslint-parser: 2.1.0
+ transitivePeerDependencies:
+ - '@eslint/json'
+ - '@typescript-eslint/typescript-estree'
+ - '@typescript-eslint/utils'
+ - '@vue/compiler-sfc'
+ - oxlint
+ - supports-color
+ - typescript
+ - vitest
+
+ '@antfu/install-pkg@1.1.0':
+ dependencies:
+ package-manager-detector: 1.5.0
+ tinyexec: 1.0.2
+
+ '@antfu/install-pkg@2.1.0':
+ dependencies:
+ package-manager-detector: 1.8.0
+ tinyexec: 1.3.1
+
+ '@arco-design/color@0.4.0':
+ dependencies:
+ color: 3.2.1
+
+ '@arco-design/web-vue@2.58.0(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@arco-design/color': 0.4.0
+ b-tween: 0.3.3
+ b-validate: 1.5.3
+ compute-scroll-into-view: 1.0.20
+ dayjs: 1.11.19
+ number-precision: 1.6.0
+ resize-observer-polyfill: 1.5.1
+ scroll-into-view-if-needed: 2.2.31
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.5': {}
+
+ '@babel/core@7.28.5':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.5
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.5':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.5
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.27.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.28.5
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.28.5
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-member-expression-to-functions@7.28.5':
+ dependencies:
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-member-expression-to-functions': 7.28.5
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.5
+
+ '@babel/parser@7.28.5':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.5)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+
+ '@babel/traverse@7.28.5':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.5
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.5
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.5
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@cacheable/memoize@2.0.3':
+ dependencies:
+ '@cacheable/utils': 2.2.0
+
+ '@cacheable/memory@2.0.4':
+ dependencies:
+ '@cacheable/utils': 2.2.0
+ '@keyv/bigmap': 1.1.0(keyv@5.5.3)
+ hookified: 1.12.2
+ keyv: 5.5.3
+
+ '@cacheable/utils@2.2.0':
+ dependencies:
+ keyv: 5.5.3
+
+ '@clack/core@1.5.1':
+ dependencies:
+ fast-wrap-ansi: 0.2.2
+ sisteransi: 1.0.5
+
+ '@clack/prompts@1.8.1':
+ dependencies:
+ '@clack/core': 1.5.1
+ fast-string-width: 3.0.2
+ fast-wrap-ansi: 0.2.2
+ sisteransi: 1.0.5
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)':
+ dependencies:
+ postcss-selector-parser: 7.1.0
+
+ '@dual-bundle/import-meta-resolve@4.2.1': {}
+
+ '@e18e/eslint-plugin@0.2.0(eslint@9.39.1)':
+ dependencies:
+ eslint-plugin-depend: 1.5.0(eslint@9.39.1)
+ optionalDependencies:
+ eslint: 9.39.1
+
+ '@es-joy/jsdoccomment@0.86.0':
+ dependencies:
+ '@types/estree': 1.0.8
+ '@typescript-eslint/types': 8.70.1
+ comment-parser: 1.4.6
+ esquery: 1.7.0
+ jsdoc-type-pratt-parser: 7.2.0
+
+ '@es-joy/jsdoccomment@0.88.0':
+ dependencies:
+ '@types/estree': 1.0.9
+ '@typescript-eslint/types': 8.70.1
+ comment-parser: 1.4.7
+ esquery: 1.7.0
+ jsdoc-type-pratt-parser: 7.2.0
+
+ '@es-joy/resolve.exports@1.2.0': {}
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm@0.25.12':
+ optional: true
+
+ '@esbuild/android-x64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.12':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.12':
+ optional: true
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.8.1(eslint@9.39.1)':
+ dependencies:
+ escape-string-regexp: 4.0.0
+ eslint: 9.39.1
+ ignore: 7.0.5
+
+ '@eslint-community/eslint-utils@4.10.1(eslint@9.39.1)':
+ dependencies:
+ eslint: 9.39.1
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)':
+ dependencies:
+ eslint: 9.39.1
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/compat@2.1.1(eslint@9.39.1)':
+ dependencies:
+ '@eslint/core': 1.2.1
+ optionalDependencies:
+ eslint: 9.39.1
+
+ '@eslint/config-array@0.21.1':
+ dependencies:
+ '@eslint/object-schema': 2.1.7
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.4.2':
+ dependencies:
+ '@eslint/core': 0.17.0
+
+ '@eslint/config-helpers@0.5.5':
+ dependencies:
+ '@eslint/core': 1.2.1
+
+ '@eslint/core@0.17.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/core@1.2.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.39.1': {}
+
+ '@eslint/markdown@7.5.1':
+ dependencies:
+ '@eslint/core': 0.17.0
+ '@eslint/plugin-kit': 0.4.1
+ github-slugger: 2.0.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-frontmatter: 2.0.1
+ mdast-util-gfm: 3.1.0
+ micromark-extension-frontmatter: 2.0.0
+ micromark-extension-gfm: 3.0.0
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/object-schema@2.1.7': {}
+
+ '@eslint/plugin-kit@0.4.1':
+ dependencies:
+ '@eslint/core': 0.17.0
+ levn: 0.4.1
+
+ '@eslint/plugin-kit@0.7.3':
+ dependencies:
+ '@eslint/core': 1.2.1
+ levn: 0.4.1
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.7':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@iconify-json/icon-park-outline@1.2.4':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@3.1.7':
+ dependencies:
+ '@antfu/install-pkg': 2.1.0
+ '@iconify/types': 2.0.0
+ import-meta-resolve: 4.2.0
+
+ '@iconify/vue@5.0.3(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@iconify/types': 2.0.0
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@keyv/bigmap@1.1.0(keyv@5.5.3)':
+ dependencies:
+ hookified: 1.12.2
+ keyv: 5.5.3
+
+ '@keyv/serialize@1.1.1': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@ota-meshi/ast-token-store@0.3.0': {}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+ optional: true
+
+ '@pkgr/core@0.3.6': {}
+
+ '@polka/url@1.0.0-next.29': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.47': {}
+
+ '@rollup/rollup-android-arm-eabi@4.52.5':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.52.5':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.52.5':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.5':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.5':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.52.5':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.52.5':
+ optional: true
+
+ '@sindresorhus/base62@1.0.0': {}
+
+ '@stylistic/eslint-plugin@5.10.0(eslint@9.39.1)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.1)
+ '@typescript-eslint/types': 8.70.1
+ eslint: 9.39.1
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ estraverse: 5.3.0
+ picomatch: 4.0.3
+
+ '@types/debug@4.1.12':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/estree@1.0.8': {}
+
+ '@types/estree@1.0.9': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/lodash-es@4.17.12':
+ dependencies:
+ '@types/lodash': 4.17.20
+
+ '@types/lodash@4.17.20': {}
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@24.10.0':
+ dependencies:
+ undici-types: 7.16.0
+
+ '@types/node@26.6.3':
+ dependencies:
+ undici-types: 8.9.0
+
+ '@types/sortablejs@1.15.9': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ '@typescript-eslint/parser': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.70.1
+ '@typescript-eslint/type-utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.70.1
+ eslint: 9.39.1
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.5.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.70.1
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/typescript-estree': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.70.1
+ debug: 4.4.3
+ eslint: 9.39.1
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.70.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.70.1
+ debug: 4.4.3
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.70.1':
+ dependencies:
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/visitor-keys': 8.70.1
+
+ '@typescript-eslint/tsconfig-utils@8.70.1(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
+ '@typescript-eslint/type-utils@8.70.1(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/typescript-estree': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ debug: 4.4.3
+ eslint: 9.39.1
+ ts-api-utils: 2.5.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.70.1': {}
+
+ '@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/visitor-keys': 8.70.1
+ debug: 4.4.3
+ minimatch: 10.2.6
+ semver: 7.7.3
+ tinyglobby: 0.2.15
+ ts-api-utils: 2.5.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.1)
+ '@typescript-eslint/scope-manager': 8.70.1
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/typescript-estree': 8.70.1(typescript@5.9.3)
+ eslint: 9.39.1
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.70.1':
+ dependencies:
+ '@typescript-eslint/types': 8.70.1
+ eslint-visitor-keys: 5.0.1
+
+ '@vitejs/plugin-vue-jsx@5.1.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5)
+ '@rolldown/pluginutils': 1.0.0-beta.47
+ '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5)
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vue: 3.5.23(typescript@5.9.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitejs/plugin-vue@6.0.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.29
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@vitest/eslint-plugin@1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.70.1
+ '@typescript-eslint/utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ eslint: 9.39.1
+ optionalDependencies:
+ '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@volar/language-core@2.4.23':
+ dependencies:
+ '@volar/source-map': 2.4.23
+
+ '@volar/source-map@2.4.23': {}
+
+ '@volar/typescript@2.4.23':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
+ '@vue/babel-helper-vue-transform-on@1.5.0': {}
+
+ '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ '@vue/babel-helper-vue-transform-on': 1.5.0
+ '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.5)
+ '@vue/shared': 3.5.23
+ optionalDependencies:
+ '@babel/core': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.28.5
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/parser': 7.28.5
+ '@vue/compiler-sfc': 3.5.23
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/compiler-core@3.5.23':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@vue/shared': 3.5.23
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.23':
+ dependencies:
+ '@vue/compiler-core': 3.5.23
+ '@vue/shared': 3.5.23
+
+ '@vue/compiler-sfc@3.5.23':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@vue/compiler-core': 3.5.23
+ '@vue/compiler-dom': 3.5.23
+ '@vue/compiler-ssr': 3.5.23
+ '@vue/shared': 3.5.23
+ estree-walker: 2.0.2
+ magic-string: 0.30.21
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.23':
+ dependencies:
+ '@vue/compiler-dom': 3.5.23
+ '@vue/shared': 3.5.23
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/devtools-api@7.7.7':
+ dependencies:
+ '@vue/devtools-kit': 7.7.7
+
+ '@vue/devtools-core@8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@vue/devtools-kit': 8.0.5
+ '@vue/devtools-shared': 8.0.5
+ mitt: 3.0.1
+ nanoid: 5.1.6
+ pathe: 2.0.3
+ vite-hot-client: 2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+ vue: 3.5.23(typescript@5.9.3)
+ transitivePeerDependencies:
+ - vite
+
+ '@vue/devtools-kit@7.7.7':
+ dependencies:
+ '@vue/devtools-shared': 7.7.7
+ birpc: 2.7.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 1.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.5
+
+ '@vue/devtools-kit@8.0.5':
+ dependencies:
+ '@vue/devtools-shared': 8.0.5
+ birpc: 2.7.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 2.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.5
+
+ '@vue/devtools-shared@7.7.7':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/devtools-shared@8.0.5':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/language-core@3.1.3(typescript@5.9.3)':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ '@vue/compiler-dom': 3.5.23
+ '@vue/shared': 3.5.23
+ alien-signals: 3.0.6
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ picomatch: 4.0.3
+ optionalDependencies:
+ typescript: 5.9.3
+
+ '@vue/reactivity@3.5.23':
+ dependencies:
+ '@vue/shared': 3.5.23
+
+ '@vue/runtime-core@3.5.23':
+ dependencies:
+ '@vue/reactivity': 3.5.23
+ '@vue/shared': 3.5.23
+
+ '@vue/runtime-dom@3.5.23':
+ dependencies:
+ '@vue/reactivity': 3.5.23
+ '@vue/runtime-core': 3.5.23
+ '@vue/shared': 3.5.23
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.23(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.23
+ '@vue/shared': 3.5.23
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@vue/shared@3.5.23': {}
+
+ '@vue/tsconfig@0.7.0(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))':
+ optionalDependencies:
+ typescript: 5.9.3
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@vueuse/core@13.9.0(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 13.9.0
+ '@vueuse/shared': 13.9.0(vue@3.5.23(typescript@5.9.3))
+ vue: 3.5.23(typescript@5.9.3)
+
+ '@vueuse/metadata@13.9.0': {}
+
+ '@vueuse/shared@13.9.0(vue@3.5.23(typescript@5.9.3))':
+ dependencies:
+ vue: 3.5.23(typescript@5.9.3)
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn-jsx@5.3.2(acorn@8.18.0):
+ dependencies:
+ acorn: 8.18.0
+
+ acorn@8.15.0: {}
+
+ acorn@8.18.0: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.17.1:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ alien-signals@3.0.6: {}
+
+ animate.css@4.1.1: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansis@4.2.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ are-docs-informative@0.0.2: {}
+
+ argparse@2.0.1: {}
+
+ array-union@2.1.0: {}
+
+ astral-regex@2.0.0: {}
+
+ asynckit@0.4.0: {}
+
+ axios@0.27.2:
+ dependencies:
+ follow-redirects: 1.15.11
+ form-data: 4.0.4
+ transitivePeerDependencies:
+ - debug
+
+ b-tween@0.3.3: {}
+
+ b-validate@1.5.3: {}
+
+ balanced-match@1.0.2: {}
+
+ balanced-match@2.0.0: {}
+
+ balanced-match@4.0.4: {}
+
+ baseline-browser-mapping@2.8.25: {}
+
+ binary-extensions@2.3.0: {}
+
+ birpc@2.7.0: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@5.0.12:
+ dependencies:
+ balanced-match: 4.0.4
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.27.0:
+ dependencies:
+ baseline-browser-mapping: 2.8.25
+ caniuse-lite: 1.0.30001754
+ electron-to-chromium: 1.5.245
+ node-releases: 2.0.27
+ update-browserslist-db: 1.1.4(browserslist@4.27.0)
+
+ builtin-modules@5.0.0: {}
+
+ bundle-name@4.1.0:
+ dependencies:
+ run-applescript: 7.1.0
+
+ cac@7.0.0: {}
+
+ cacheable@2.1.1:
+ dependencies:
+ '@cacheable/memoize': 2.0.3
+ '@cacheable/memory': 2.0.4
+ '@cacheable/utils': 2.2.0
+ hookified: 1.12.2
+ keyv: 5.5.3
+ qified: 0.5.1
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001754: {}
+
+ ccount@2.0.1: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ change-case@5.4.4: {}
+
+ character-entities@2.0.2: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ ci-info@4.3.1: {}
+
+ clean-regexp@1.0.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.4
+
+ color@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+ color-string: 1.9.1
+
+ colord@2.9.3: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ comment-parser@1.4.1: {}
+
+ comment-parser@1.4.6: {}
+
+ comment-parser@1.4.7: {}
+
+ compute-scroll-into-view@1.0.20: {}
+
+ concat-map@0.0.1: {}
+
+ confbox@0.1.8: {}
+
+ confbox@0.2.2: {}
+
+ convert-source-map@2.0.0: {}
+
+ copy-anything@2.0.6:
+ dependencies:
+ is-what: 3.14.1
+
+ copy-anything@4.0.5:
+ dependencies:
+ is-what: 5.5.0
+
+ core-js-compat@3.46.0:
+ dependencies:
+ browserslist: 4.27.0
+
+ cosmiconfig@9.0.0(typescript@5.9.3):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 5.9.3
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-functions-list@3.2.3: {}
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ cssesc@3.0.0: {}
+
+ csstype@3.1.3: {}
+
+ dayjs@1.11.19: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.2.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ deep-is@0.1.4: {}
+
+ default-browser-id@5.0.1: {}
+
+ default-browser@5.4.0:
+ dependencies:
+ bundle-name: 4.1.0
+ default-browser-id: 5.0.1
+
+ define-lazy-prop@3.0.0: {}
+
+ defu@6.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ dequal@2.0.3: {}
+
+ detect-libc@1.0.3:
+ optional: true
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ diff-sequences@29.6.3: {}
+
+ dir-glob@3.0.1:
+ dependencies:
+ path-type: 4.0.0
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ echarts@6.1.0:
+ dependencies:
+ tslib: 2.3.0
+ zrender: 6.1.0
+
+ electron-to-chromium@1.5.245: {}
+
+ emoji-regex@8.0.0: {}
+
+ empathic@2.0.0: {}
+
+ empathic@2.1.0: {}
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ entities@4.5.0: {}
+
+ env-paths@2.2.1: {}
+
+ errno@0.1.8:
+ dependencies:
+ prr: 1.0.1
+ optional: true
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ error-stack-parser-es@1.0.5: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ esbuild@0.25.12:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.12
+ '@esbuild/android-arm': 0.25.12
+ '@esbuild/android-arm64': 0.25.12
+ '@esbuild/android-x64': 0.25.12
+ '@esbuild/darwin-arm64': 0.25.12
+ '@esbuild/darwin-x64': 0.25.12
+ '@esbuild/freebsd-arm64': 0.25.12
+ '@esbuild/freebsd-x64': 0.25.12
+ '@esbuild/linux-arm': 0.25.12
+ '@esbuild/linux-arm64': 0.25.12
+ '@esbuild/linux-ia32': 0.25.12
+ '@esbuild/linux-loong64': 0.25.12
+ '@esbuild/linux-mips64el': 0.25.12
+ '@esbuild/linux-ppc64': 0.25.12
+ '@esbuild/linux-riscv64': 0.25.12
+ '@esbuild/linux-s390x': 0.25.12
+ '@esbuild/linux-x64': 0.25.12
+ '@esbuild/netbsd-arm64': 0.25.12
+ '@esbuild/netbsd-x64': 0.25.12
+ '@esbuild/openbsd-arm64': 0.25.12
+ '@esbuild/openbsd-x64': 0.25.12
+ '@esbuild/openharmony-arm64': 0.25.12
+ '@esbuild/sunos-x64': 0.25.12
+ '@esbuild/win32-arm64': 0.25.12
+ '@esbuild/win32-ia32': 0.25.12
+ '@esbuild/win32-x64': 0.25.12
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ eslint-compat-utils@0.5.1(eslint@9.39.1):
+ dependencies:
+ eslint: 9.39.1
+ semver: 7.7.3
+
+ eslint-config-flat-gitignore@2.4.0(eslint@9.39.1):
+ dependencies:
+ '@eslint/compat': 2.1.1(eslint@9.39.1)
+ eslint: 9.39.1
+
+ eslint-flat-config-utils@3.2.0:
+ dependencies:
+ '@eslint/config-helpers': 0.5.5
+ pathe: 2.0.3
+
+ eslint-json-compat-utils@0.2.3(eslint@9.39.1)(jsonc-eslint-parser@3.3.0):
+ dependencies:
+ eslint: 9.39.1
+ esquery: 1.6.0
+ jsonc-eslint-parser: 3.3.0
+
+ eslint-merge-processors@2.0.0(eslint@9.39.1):
+ dependencies:
+ eslint: 9.39.1
+
+ eslint-plugin-antfu@3.2.3(eslint@9.39.1):
+ dependencies:
+ eslint: 9.39.1
+
+ eslint-plugin-command@3.5.3(@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3))(@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.88.0
+ '@typescript-eslint/typescript-estree': 8.70.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ eslint: 9.39.1
+
+ eslint-plugin-depend@1.5.0(eslint@9.39.1):
+ dependencies:
+ empathic: 2.0.0
+ eslint: 9.39.1
+ module-replacements: 2.11.0
+ semver: 7.7.3
+
+ eslint-plugin-es-x@7.8.0(eslint@9.39.1):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ '@eslint-community/regexpp': 4.12.2
+ eslint: 9.39.1
+ eslint-compat-utils: 0.5.1(eslint@9.39.1)
+
+ eslint-plugin-import-lite@0.5.2(eslint@9.39.1):
+ dependencies:
+ eslint: 9.39.1
+
+ eslint-plugin-jsdoc@62.9.0(eslint@9.39.1):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.86.0
+ '@es-joy/resolve.exports': 1.2.0
+ are-docs-informative: 0.0.2
+ comment-parser: 1.4.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint: 9.39.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ html-entities: 2.6.0
+ object-deep-merge: 2.0.1
+ parse-imports-exports: 0.2.4
+ semver: 7.8.5
+ spdx-expression-parse: 4.0.0
+ to-valid-identifier: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-jsonc@3.4.2(eslint@9.39.1):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.3
+ '@ota-meshi/ast-token-store': 0.3.0
+ diff-sequences: 29.6.3
+ eslint: 9.39.1
+ eslint-json-compat-utils: 0.2.3(eslint@9.39.1)(jsonc-eslint-parser@3.3.0)
+ jsonc-eslint-parser: 3.3.0
+ natural-compare: 1.4.0
+ synckit: 0.11.13
+ transitivePeerDependencies:
+ - '@eslint/json'
+
+ eslint-plugin-n@17.24.0(eslint@9.39.1)(typescript@5.9.3):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ enhanced-resolve: 5.18.3
+ eslint: 9.39.1
+ eslint-plugin-es-x: 7.8.0(eslint@9.39.1)
+ get-tsconfig: 4.13.0
+ globals: 15.15.0
+ globrex: 0.1.2
+ ignore: 5.3.2
+ semver: 7.7.3
+ ts-declaration-location: 1.0.7(typescript@5.9.3)
+ transitivePeerDependencies:
+ - typescript
+
+ eslint-plugin-no-only-tests@3.3.0: {}
+
+ eslint-plugin-perfectionist@5.12.1(eslint@9.39.1)(typescript@5.9.3):
+ dependencies:
+ '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/utils': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+ natural-orderby: 5.0.0
+ optionalDependencies:
+ eslint: 9.39.1
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-pnpm@1.9.1(eslint@9.39.1):
+ dependencies:
+ empathic: 2.1.0
+ eslint: 9.39.1
+ eslint-json-compat-utils: 0.2.3(eslint@9.39.1)(jsonc-eslint-parser@3.3.0)
+ jsonc-eslint-parser: 3.3.0
+ pathe: 2.0.3
+ pnpm-workspace-yaml: 1.9.1
+ tinyglobby: 0.2.17
+ yaml: 2.9.1
+ yaml-eslint-parser: 2.1.0
+ transitivePeerDependencies:
+ - '@eslint/json'
+
+ eslint-plugin-regexp@3.3.1(eslint@9.39.1):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ '@eslint-community/regexpp': 4.12.2
+ comment-parser: 1.4.1
+ eslint: 9.39.1
+ jsdoc-type-pratt-parser: 9.2.2
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+ scslre: 0.3.0
+
+ eslint-plugin-toml@1.5.0(eslint@9.39.1):
+ dependencies:
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.3
+ '@ota-meshi/ast-token-store': 0.3.0
+ debug: 4.4.3
+ eslint: 9.39.1
+ toml-eslint-parser: 1.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-unicorn@63.0.0(eslint@9.39.1):
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ change-case: 5.4.4
+ ci-info: 4.3.1
+ clean-regexp: 1.0.0
+ core-js-compat: 3.46.0
+ eslint: 9.39.1
+ find-up-simple: 1.0.1
+ globals: 16.5.0
+ indent-string: 5.0.0
+ is-builtin-module: 5.0.0
+ jsesc: 3.1.0
+ pluralize: 8.0.0
+ regexp-tree: 0.1.27
+ regjsparser: 0.13.3
+ semver: 7.7.3
+ strip-indent: 4.1.1
+
+ eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1):
+ dependencies:
+ eslint: 9.39.1
+ optionalDependencies:
+ '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+
+ eslint-plugin-vue@10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@9.39.1))(@typescript-eslint/parser@8.70.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.4.1(eslint@9.39.1)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.1)
+ eslint: 9.39.1
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 7.1.6
+ semver: 7.8.5
+ vue-eslint-parser: 10.4.1(eslint@9.39.1)
+ xml-name-validator: 5.0.0
+ optionalDependencies:
+ '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.1)
+ '@typescript-eslint/parser': 8.70.1(eslint@9.39.1)(typescript@5.9.3)
+
+ eslint-plugin-yml@3.8.1(eslint@9.39.1):
+ dependencies:
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.3
+ '@ota-meshi/ast-token-store': 0.3.0
+ diff-sequences: 29.6.3
+ escape-string-regexp: 5.0.0
+ eslint: 9.39.1
+ natural-compare: 1.4.0
+ yaml-eslint-parser: 2.1.0
+
+ eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.23)(eslint@9.39.1):
+ dependencies:
+ '@vue/compiler-sfc': 3.5.23
+ eslint: 9.39.1
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@9.39.1:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.21.1
+ '@eslint/config-helpers': 0.4.2
+ '@eslint/core': 0.17.0
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.39.1
+ '@eslint/plugin-kit': 0.4.1
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ espree@11.2.0:
+ dependencies:
+ acorn: 8.18.0
+ acorn-jsx: 5.3.2(acorn@8.18.0)
+ eslint-visitor-keys: 5.0.1
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ esutils@2.0.3: {}
+
+ exsolve@1.0.7: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-string-truncated-width@3.0.3: {}
+
+ fast-string-width@3.0.2:
+ dependencies:
+ fast-string-truncated-width: 3.0.3
+
+ fast-uri@3.1.0: {}
+
+ fast-wrap-ansi@0.2.2:
+ dependencies:
+ fast-string-width: 3.0.2
+
+ fastest-levenshtein@1.0.16: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fault@2.0.1:
+ dependencies:
+ format: 0.2.2
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fdir@6.5.0(picomatch@4.0.7):
+ optionalDependencies:
+ picomatch: 4.0.7
+
+ file-entry-cache@10.1.4:
+ dependencies:
+ flat-cache: 6.1.18
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up-simple@1.0.1: {}
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flat-cache@6.1.18:
+ dependencies:
+ cacheable: 2.1.1
+ flatted: 3.3.3
+ hookified: 1.12.2
+
+ flatted@3.3.3: {}
+
+ follow-redirects@1.15.11: {}
+
+ form-data@4.0.4:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ format@0.2.2: {}
+
+ fs-extra@10.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-tsconfig@4.13.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ github-slugger@2.0.0: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ global-modules@2.0.0:
+ dependencies:
+ global-prefix: 3.0.0
+
+ global-prefix@3.0.0:
+ dependencies:
+ ini: 1.3.8
+ kind-of: 6.0.3
+ which: 1.3.1
+
+ globals@14.0.0: {}
+
+ globals@15.15.0: {}
+
+ globals@16.5.0: {}
+
+ globals@17.12.0: {}
+
+ globby@11.1.0:
+ dependencies:
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.3
+ ignore: 5.3.2
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ globjoin@0.1.4: {}
+
+ globrex@0.1.2: {}
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ has-flag@4.0.0: {}
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hookable@5.5.3: {}
+
+ hookified@1.12.2: {}
+
+ html-entities@2.6.0: {}
+
+ html-tags@3.3.1: {}
+
+ htmlparser2@8.0.2:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ entities: 4.5.0
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+ optional: true
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ image-size@0.5.5:
+ optional: true
+
+ immutable@5.1.4: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ import-meta-resolve@4.2.0: {}
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@5.0.0: {}
+
+ ini@1.3.8: {}
+
+ is-arrayish@0.2.1: {}
+
+ is-arrayish@0.3.4: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-builtin-module@5.0.0:
+ dependencies:
+ builtin-modules: 5.0.0
+
+ is-docker@3.0.0: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-inside-container@1.0.0:
+ dependencies:
+ is-docker: 3.0.0
+
+ is-number@7.0.0: {}
+
+ is-plain-object@5.0.0: {}
+
+ is-what@3.14.1: {}
+
+ is-what@5.5.0: {}
+
+ is-wsl@3.1.0:
+ dependencies:
+ is-inside-container: 1.0.0
+
+ isexe@2.0.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-tokens@9.0.1: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsdoc-type-pratt-parser@7.2.0: {}
+
+ jsdoc-type-pratt-parser@9.2.2:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/node': 26.6.3
+
+ jsencrypt@3.5.4: {}
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@2.2.3: {}
+
+ jsonc-eslint-parser@3.3.0:
+ dependencies:
+ acorn: 8.15.0
+ eslint-visitor-keys: 5.0.1
+ verkit: 0.3.2
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ keyv@5.5.3:
+ dependencies:
+ '@keyv/serialize': 1.1.1
+
+ kind-of@6.0.3: {}
+
+ known-css-properties@0.36.0: {}
+
+ known-css-properties@0.37.0: {}
+
+ kolorist@1.8.0: {}
+
+ less@4.4.2:
+ dependencies:
+ copy-anything: 2.0.6
+ parse-node-version: 1.0.1
+ tslib: 2.8.1
+ optionalDependencies:
+ errno: 0.1.8
+ graceful-fs: 4.2.11
+ image-size: 0.5.5
+ make-dir: 2.1.0
+ mime: 1.6.0
+ needle: 3.3.1
+ source-map: 0.6.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lines-and-columns@1.2.4: {}
+
+ local-pkg@1.1.2:
+ dependencies:
+ mlly: 1.8.0
+ pkg-types: 2.3.0
+ quansync: 0.2.11
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash-es@4.17.21: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.truncate@4.4.2: {}
+
+ longest-streak@3.1.0: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ make-dir@2.1.0:
+ dependencies:
+ pify: 4.0.1
+ semver: 5.7.2
+ optional: true
+
+ markdown-table@3.0.4: {}
+
+ math-intrinsics@1.1.0: {}
+
+ mathml-tag-names@2.1.3: {}
+
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ mdast-util-from-markdown@2.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.2
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-frontmatter@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ escape-string-regexp: 5.0.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ micromark-extension-frontmatter: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.1
+
+ mdast-util-to-markdown@2.1.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ unist-util-visit: 5.0.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ mdn-data@2.12.2: {}
+
+ mdn-data@2.25.0: {}
+
+ meow@13.2.0: {}
+
+ merge2@1.4.1: {}
+
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-frontmatter@2.0.0:
+ dependencies:
+ fault: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.2.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.12
+ debug: 4.4.3
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mime@1.6.0:
+ optional: true
+
+ minimatch@10.2.6:
+ dependencies:
+ brace-expansion: 5.0.12
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ mitt@3.0.1: {}
+
+ mlly@1.8.0:
+ dependencies:
+ acorn: 8.15.0
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.6.1
+
+ module-replacements@2.11.0: {}
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.11: {}
+
+ nanoid@5.1.6: {}
+
+ natural-compare@1.4.0: {}
+
+ natural-orderby@5.0.0: {}
+
+ needle@3.3.1:
+ dependencies:
+ iconv-lite: 0.6.3
+ sax: 1.4.3
+ optional: true
+
+ node-addon-api@7.1.1:
+ optional: true
+
+ node-releases@2.0.27: {}
+
+ normalize-path@3.0.0: {}
+
+ nprogress@0.2.0: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ number-precision@1.6.0: {}
+
+ object-deep-merge@2.0.1: {}
+
+ ohash@2.0.11: {}
+
+ open@10.2.0:
+ dependencies:
+ default-browser: 5.4.0
+ define-lazy-prop: 3.0.0
+ is-inside-container: 1.0.0
+ wsl-utils: 0.1.0
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ package-manager-detector@1.5.0: {}
+
+ package-manager-detector@1.8.0: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-gitignore@2.0.0: {}
+
+ parse-imports-exports@0.2.4:
+ dependencies:
+ parse-statements: 1.0.11
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ parse-node-version@1.0.1: {}
+
+ parse-statements@1.0.11: {}
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-type@4.0.0: {}
+
+ pathe@2.0.3: {}
+
+ perfect-debounce@1.0.0: {}
+
+ perfect-debounce@2.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ picomatch@4.0.7: {}
+
+ pify@4.0.1:
+ optional: true
+
+ pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))):
+ dependencies:
+ defu: 6.1.4
+ optionalDependencies:
+ pinia: 3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))
+
+ pinia@3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3)):
+ dependencies:
+ '@vue/devtools-api': 7.7.7
+ vue: 3.5.23(typescript@5.9.3)
+ optionalDependencies:
+ typescript: 5.9.3
+
+ pkg-types@1.3.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.8.0
+ pathe: 2.0.3
+
+ pkg-types@2.3.0:
+ dependencies:
+ confbox: 0.2.2
+ exsolve: 1.0.7
+ pathe: 2.0.3
+
+ pluralize@8.0.0: {}
+
+ pnpm-workspace-yaml@1.9.1:
+ dependencies:
+ yaml: 2.9.1
+
+ postcss-html@1.8.0:
+ dependencies:
+ htmlparser2: 8.0.2
+ js-tokens: 9.0.1
+ postcss: 8.5.6
+ postcss-safe-parser: 6.0.0(postcss@8.5.6)
+
+ postcss-media-query-parser@0.2.3: {}
+
+ postcss-resolve-nested-selector@0.1.6: {}
+
+ postcss-safe-parser@6.0.0(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-safe-parser@7.0.1(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-scss@4.0.9(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-selector-parser@7.1.0:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@7.1.6:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-sorting@9.1.0(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prr@1.0.1:
+ optional: true
+
+ punycode@2.3.1: {}
+
+ qified@0.5.1:
+ dependencies:
+ hookified: 1.12.2
+
+ quansync@0.2.11: {}
+
+ queue-microtask@1.2.3: {}
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@4.1.2: {}
+
+ refa@0.12.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+
+ regexp-ast-analysis@0.7.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ refa: 0.12.1
+
+ regexp-tree@0.1.27: {}
+
+ regjsparser@0.13.3:
+ dependencies:
+ jsesc: 3.1.0
+
+ require-from-string@2.0.2: {}
+
+ reserved-identifiers@1.2.0: {}
+
+ resize-observer-polyfill@1.5.1: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ reusify@1.1.0: {}
+
+ rfdc@1.4.1: {}
+
+ rollup@4.52.5:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.52.5
+ '@rollup/rollup-android-arm64': 4.52.5
+ '@rollup/rollup-darwin-arm64': 4.52.5
+ '@rollup/rollup-darwin-x64': 4.52.5
+ '@rollup/rollup-freebsd-arm64': 4.52.5
+ '@rollup/rollup-freebsd-x64': 4.52.5
+ '@rollup/rollup-linux-arm-gnueabihf': 4.52.5
+ '@rollup/rollup-linux-arm-musleabihf': 4.52.5
+ '@rollup/rollup-linux-arm64-gnu': 4.52.5
+ '@rollup/rollup-linux-arm64-musl': 4.52.5
+ '@rollup/rollup-linux-loong64-gnu': 4.52.5
+ '@rollup/rollup-linux-ppc64-gnu': 4.52.5
+ '@rollup/rollup-linux-riscv64-gnu': 4.52.5
+ '@rollup/rollup-linux-riscv64-musl': 4.52.5
+ '@rollup/rollup-linux-s390x-gnu': 4.52.5
+ '@rollup/rollup-linux-x64-gnu': 4.52.5
+ '@rollup/rollup-linux-x64-musl': 4.52.5
+ '@rollup/rollup-openharmony-arm64': 4.52.5
+ '@rollup/rollup-win32-arm64-msvc': 4.52.5
+ '@rollup/rollup-win32-ia32-msvc': 4.52.5
+ '@rollup/rollup-win32-x64-gnu': 4.52.5
+ '@rollup/rollup-win32-x64-msvc': 4.52.5
+ fsevents: 2.3.3
+
+ run-applescript@7.1.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safer-buffer@2.1.2:
+ optional: true
+
+ sass@1.93.3:
+ dependencies:
+ chokidar: 4.0.3
+ immutable: 5.1.4
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.1
+
+ sax@1.4.3:
+ optional: true
+
+ scroll-into-view-if-needed@2.2.31:
+ dependencies:
+ compute-scroll-into-view: 1.0.20
+
+ scslre@0.3.0:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+
+ scule@1.3.0: {}
+
+ semver@5.7.2:
+ optional: true
+
+ semver@6.3.1: {}
+
+ semver@7.7.3: {}
+
+ semver@7.8.5: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ simple-swizzle@0.2.4:
+ dependencies:
+ is-arrayish: 0.3.4
+
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ sisteransi@1.0.5: {}
+
+ slash@3.0.0: {}
+
+ slice-ansi@4.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
+ sortablejs@1.15.6: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map@0.6.1:
+ optional: true
+
+ spdx-exceptions@2.5.0: {}
+
+ spdx-expression-parse@4.0.0:
+ dependencies:
+ spdx-exceptions: 2.5.0
+ spdx-license-ids: 3.0.22
+
+ spdx-license-ids@3.0.22: {}
+
+ speakingurl@14.0.1: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-indent@4.1.1: {}
+
+ strip-json-comments@3.1.1: {}
+
+ strip-literal@3.1.0:
+ dependencies:
+ js-tokens: 9.0.1
+
+ stylelint-config-html@1.1.0(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ postcss-html: 1.8.0
+ stylelint: 16.25.0(typescript@5.9.3)
+
+ stylelint-config-recess-order@7.4.0(stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)))(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ stylelint: 16.25.0(typescript@5.9.3)
+ stylelint-order: 7.0.0(stylelint@16.25.0(typescript@5.9.3))
+
+ stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ postcss-scss: 4.0.9(postcss@8.5.6)
+ stylelint: 16.25.0(typescript@5.9.3)
+ stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-scss: 6.12.1(stylelint@16.25.0(typescript@5.9.3))
+ optionalDependencies:
+ postcss: 8.5.6
+
+ stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ postcss-html: 1.8.0
+ semver: 7.7.3
+ stylelint: 16.25.0(typescript@5.9.3)
+ stylelint-config-html: 1.1.0(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3))
+
+ stylelint-config-recommended@17.0.0(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ stylelint: 16.25.0(typescript@5.9.3)
+
+ stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ stylelint: 16.25.0(typescript@5.9.3)
+ stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3))
+ stylelint-config-standard: 39.0.1(stylelint@16.25.0(typescript@5.9.3))
+ optionalDependencies:
+ postcss: 8.5.6
+
+ stylelint-config-standard@39.0.1(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ stylelint: 16.25.0(typescript@5.9.3)
+ stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3))
+
+ stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ postcss: 8.5.6
+ postcss-sorting: 9.1.0(postcss@8.5.6)
+ stylelint: 16.25.0(typescript@5.9.3)
+
+ stylelint-scss@6.12.1(stylelint@16.25.0(typescript@5.9.3)):
+ dependencies:
+ css-tree: 3.1.0
+ is-plain-object: 5.0.0
+ known-css-properties: 0.36.0
+ mdn-data: 2.25.0
+ postcss-media-query-parser: 0.2.3
+ postcss-resolve-nested-selector: 0.1.6
+ postcss-selector-parser: 7.1.0
+ postcss-value-parser: 4.2.0
+ stylelint: 16.25.0(typescript@5.9.3)
+
+ stylelint@16.25.0(typescript@5.9.3):
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0)
+ '@dual-bundle/import-meta-resolve': 4.2.1
+ balanced-match: 2.0.0
+ colord: 2.9.3
+ cosmiconfig: 9.0.0(typescript@5.9.3)
+ css-functions-list: 3.2.3
+ css-tree: 3.1.0
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ fastest-levenshtein: 1.0.16
+ file-entry-cache: 10.1.4
+ global-modules: 2.0.0
+ globby: 11.1.0
+ globjoin: 0.1.4
+ html-tags: 3.3.1
+ ignore: 7.0.5
+ imurmurhash: 0.1.4
+ is-plain-object: 5.0.0
+ known-css-properties: 0.37.0
+ mathml-tag-names: 2.1.3
+ meow: 13.2.0
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.6
+ postcss-resolve-nested-selector: 0.1.6
+ postcss-safe-parser: 7.0.1(postcss@8.5.6)
+ postcss-selector-parser: 7.1.0
+ postcss-value-parser: 4.2.0
+ resolve-from: 5.0.0
+ string-width: 4.2.3
+ supports-hyperlinks: 3.2.0
+ svg-tags: 1.0.0
+ table: 6.9.0
+ write-file-atomic: 5.0.1
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ superjson@2.2.5:
+ dependencies:
+ copy-anything: 4.0.5
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-hyperlinks@3.2.0:
+ dependencies:
+ has-flag: 4.0.0
+ supports-color: 7.2.0
+
+ svg-tags@1.0.0: {}
+
+ synckit@0.11.13:
+ dependencies:
+ '@pkgr/core': 0.3.6
+
+ table@6.9.0:
+ dependencies:
+ ajv: 8.17.1
+ lodash.truncate: 4.4.2
+ slice-ansi: 4.0.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ tapable@2.3.0: {}
+
+ tinyexec@1.0.2: {}
+
+ tinyexec@1.3.1: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.7)
+ picomatch: 4.0.7
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ to-valid-identifier@1.0.0:
+ dependencies:
+ '@sindresorhus/base62': 1.0.0
+ reserved-identifiers: 1.2.0
+
+ toml-eslint-parser@1.0.3:
+ dependencies:
+ eslint-visitor-keys: 5.0.1
+
+ totalist@3.0.1: {}
+
+ ts-api-utils@2.5.0(typescript@5.9.3):
+ dependencies:
+ typescript: 5.9.3
+
+ ts-declaration-location@1.0.7(typescript@5.9.3):
+ dependencies:
+ picomatch: 4.0.3
+ typescript: 5.9.3
+
+ tslib@2.3.0: {}
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typescript@5.9.3: {}
+
+ ufo@1.6.1: {}
+
+ undici-types@7.16.0: {}
+
+ undici-types@8.9.0: {}
+
+ unimport@5.5.0:
+ dependencies:
+ acorn: 8.15.0
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ local-pkg: 1.1.2
+ magic-string: 0.30.21
+ mlly: 1.8.0
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ pkg-types: 2.3.0
+ scule: 1.3.0
+ strip-literal: 3.1.0
+ tinyglobby: 0.2.15
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.1
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ universalify@2.0.1: {}
+
+ unplugin-auto-import@20.2.0(@vueuse/core@13.9.0(vue@3.5.23(typescript@5.9.3))):
+ dependencies:
+ local-pkg: 1.1.2
+ magic-string: 0.30.21
+ picomatch: 4.0.3
+ unimport: 5.5.0
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.1
+ optionalDependencies:
+ '@vueuse/core': 13.9.0(vue@3.5.23(typescript@5.9.3))
+
+ unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.23):
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@iconify/utils': 3.1.7
+ debug: 4.4.3
+ local-pkg: 1.1.2
+ unplugin: 2.3.10
+ optionalDependencies:
+ '@vue/compiler-sfc': 3.5.23
+ transitivePeerDependencies:
+ - supports-color
+
+ unplugin-utils@0.3.1:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.3
+
+ unplugin-vue-components@29.2.0(@babel/parser@7.28.5)(vue@3.5.23(typescript@5.9.3)):
+ dependencies:
+ chokidar: 3.6.0
+ debug: 4.4.3
+ local-pkg: 1.1.2
+ magic-string: 0.30.21
+ mlly: 1.8.0
+ tinyglobby: 0.2.15
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.1
+ vue: 3.5.23(typescript@5.9.3)
+ optionalDependencies:
+ '@babel/parser': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ unplugin@2.3.10:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ acorn: 8.15.0
+ picomatch: 4.0.3
+ webpack-virtual-modules: 0.6.2
+
+ update-browserslist-db@1.1.4(browserslist@4.27.0):
+ dependencies:
+ browserslist: 4.27.0
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ verkit@0.3.2: {}
+
+ vite-dev-rpc@1.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)):
+ dependencies:
+ birpc: 2.7.0
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vite-hot-client: 2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+
+ vite-hot-client@2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)):
+ dependencies:
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+
+ vite-plugin-compression@0.5.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)):
+ dependencies:
+ chalk: 4.1.2
+ debug: 4.4.3
+ fs-extra: 10.1.0
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ vite-plugin-inspect@11.3.3(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)):
+ dependencies:
+ ansis: 4.2.0
+ debug: 4.4.3
+ error-stack-parser-es: 1.0.5
+ ohash: 2.0.11
+ open: 10.2.0
+ perfect-debounce: 2.0.0
+ sirv: 3.0.2
+ unplugin-utils: 0.3.1
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vite-dev-rpc: 1.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+ transitivePeerDependencies:
+ - supports-color
+
+ vite-plugin-vue-devtools@8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)):
+ dependencies:
+ '@vue/devtools-core': 8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))
+ '@vue/devtools-kit': 8.0.5
+ '@vue/devtools-shared': 8.0.5
+ sirv: 3.0.2
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ vite-plugin-inspect: 11.3.3(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+ vite-plugin-vue-inspector: 5.3.2(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - supports-color
+ - vue
+
+ vite-plugin-vue-inspector@5.3.2(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)):
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5)
+ '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5)
+ '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5)
+ '@vue/compiler-dom': 3.5.23
+ kolorist: 1.8.0
+ magic-string: 0.30.21
+ vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1):
+ dependencies:
+ esbuild: 0.25.12
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.52.5
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 24.10.0
+ fsevents: 2.3.3
+ less: 4.4.2
+ sass: 1.93.3
+ yaml: 2.9.1
+
+ vscode-uri@3.1.0: {}
+
+ vue-color-kit@1.0.6(vue@3.5.23(typescript@5.9.3)):
+ dependencies:
+ vue: 3.5.23(typescript@5.9.3)
+
+ vue-draggable-plus@0.6.0(@types/sortablejs@1.15.9):
+ dependencies:
+ '@types/sortablejs': 1.15.9
+
+ vue-eslint-parser@10.4.1(eslint@9.39.1):
+ dependencies:
+ debug: 4.4.3
+ eslint: 9.39.1
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ semver: 7.7.3
+ transitivePeerDependencies:
+ - supports-color
+
+ vue-router@4.6.3(vue@3.5.23(typescript@5.9.3)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.23(typescript@5.9.3)
+
+ vue-tsc@3.1.3(typescript@5.9.3):
+ dependencies:
+ '@volar/typescript': 2.4.23
+ '@vue/language-core': 3.1.3(typescript@5.9.3)
+ typescript: 5.9.3
+
+ vue@3.5.23(typescript@5.9.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.23
+ '@vue/compiler-sfc': 3.5.23
+ '@vue/runtime-dom': 3.5.23
+ '@vue/server-renderer': 3.5.23(vue@3.5.23(typescript@5.9.3))
+ '@vue/shared': 3.5.23
+ optionalDependencies:
+ typescript: 5.9.3
+
+ webpack-virtual-modules@0.6.2: {}
+
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ write-file-atomic@5.0.1:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 4.1.0
+
+ wsl-utils@0.1.0:
+ dependencies:
+ is-wsl: 3.1.0
+
+ xe-utils@3.7.9: {}
+
+ xml-name-validator@5.0.0: {}
+
+ yallist@3.1.1: {}
+
+ yaml-eslint-parser@2.1.0:
+ dependencies:
+ eslint-visitor-keys: 5.0.1
+ yaml: 2.8.1
+
+ yaml@2.8.1: {}
+
+ yaml@2.9.1: {}
+
+ yocto-queue@0.1.0: {}
+
+ zrender@6.1.0:
+ dependencies:
+ tslib: 2.3.0
+
+ zwitch@2.0.4: {}
diff --git a/frontend-pc/public/favicon.svg b/frontend-pc/public/favicon.svg
new file mode 100644
index 0000000..b7fd708
--- /dev/null
+++ b/frontend-pc/public/favicon.svg
@@ -0,0 +1,19 @@
+
diff --git a/frontend-pc/public/static/css/loading.css b/frontend-pc/public/static/css/loading.css
new file mode 100644
index 0000000..2cbc8f2
--- /dev/null
+++ b/frontend-pc/public/static/css/loading.css
@@ -0,0 +1,55 @@
+/**
+ * 首屏骨架样式。
+ *
+ * 独立成文件(不经构建),因为它要在 JS 包下载、执行之前就生效 ——
+ * 放进 src/ 走打包的话,这段样式反而会晚于首屏出现。
+ *
+ * 颜色用腾讯云主色 #0052D9(与 src/styles/arco-ui/xt-theme.less 一致);
+ * 这里不能写 var(--primary-6),因为此时 Arco 的样式表还没加载。
+ */
+
+body {
+ overflow: hidden;
+}
+
+.init-box {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100vw;
+ height: 100vh;
+ background: #f3f3f3;
+}
+
+.init-loading {
+ display: flex;
+ justify-content: space-between;
+ width: 40px;
+ height: 40px;
+}
+
+.init-loading-rect {
+ width: 15%;
+ height: 100%;
+ background-color: #0052d9;
+ border-radius: 2px;
+ animation: init-wave 1.2s infinite ease-in-out;
+}
+
+.init-loading-rect:nth-child(1) { animation-delay: -1.2s; }
+.init-loading-rect:nth-child(2) { animation-delay: -1.1s; }
+.init-loading-rect:nth-child(3) { animation-delay: -1s; }
+.init-loading-rect:nth-child(4) { animation-delay: -0.9s; }
+.init-loading-rect:nth-child(5) { animation-delay: -0.8s; }
+
+@keyframes init-wave {
+ 0%,
+ 40%,
+ 100% {
+ transform: scaleY(0.4);
+ }
+
+ 20% {
+ transform: scaleY(1);
+ }
+}
diff --git a/frontend-pc/src/App.vue b/frontend-pc/src/App.vue
new file mode 100644
index 0000000..3d51d7f
--- /dev/null
+++ b/frontend-pc/src/App.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/apis/area/index.ts b/frontend-pc/src/apis/area/index.ts
new file mode 100644
index 0000000..f526bff
--- /dev/null
+++ b/frontend-pc/src/apis/area/index.ts
@@ -0,0 +1,9 @@
+import type * as T from './type'
+import http from '@/utils/http'
+
+export type * from './type'
+
+/** 获取地区列表 */
+export const getProvinceCityArea = (params: { type: 'province' | 'city' | 'area', code?: string }) => {
+ return http.get('/area/getProvinceCityArea', params)
+}
diff --git a/frontend-pc/src/apis/area/type.ts b/frontend-pc/src/apis/area/type.ts
new file mode 100644
index 0000000..f129e36
--- /dev/null
+++ b/frontend-pc/src/apis/area/type.ts
@@ -0,0 +1,5 @@
+export interface AreaItem {
+ label: string
+ code: string
+ children?: AreaItem[]
+}
diff --git a/frontend-pc/src/apis/base.ts b/frontend-pc/src/apis/base.ts
new file mode 100644
index 0000000..cfc05ec
--- /dev/null
+++ b/frontend-pc/src/apis/base.ts
@@ -0,0 +1,39 @@
+import http from '@/utils/http'
+
+interface DefaultP {
+ GetListParams?: Record
+ GetDetailParams?: { id: string }
+ AddParams?: Record
+ UpdateParams?: Record
+ DeleteParams?: { ids: string[] }
+}
+
+// 获取基础接口
+export function getBaseApi(params: { baseUrl: string }) {
+ const { baseUrl } = params
+
+ const baseApi = {
+ // 获取分页列表
+ getList(params?: P['GetListParams'] & { page?: number, size?: number }) {
+ return http.get>(`${baseUrl}/getList`, params)
+ },
+ // 获取详情
+ getDetail(params: P['GetDetailParams']) {
+ return http.get(`${baseUrl}/getDetail`, params)
+ },
+ // 新增
+ add(params: P['AddParams']) {
+ return http.post(`${baseUrl}/add`, params)
+ },
+ // 修改
+ update(params: P['UpdateParams']) {
+ return http.post(`${baseUrl}/update`, params)
+ },
+ // 删除
+ delete(params: P['DeleteParams']) {
+ return http.post(`${baseUrl}/delete`, params)
+ }
+ }
+
+ return baseApi
+}
diff --git a/frontend-pc/src/apis/cate/index.ts b/frontend-pc/src/apis/cate/index.ts
new file mode 100644
index 0000000..c1aa836
--- /dev/null
+++ b/frontend-pc/src/apis/cate/index.ts
@@ -0,0 +1,9 @@
+import type * as T from './type'
+import http from '@/utils/http'
+
+export type * from './type'
+
+/** 获取分类树 */
+export function getCateTree() {
+ return http.get('/cate/getCateTree')
+}
diff --git a/frontend-pc/src/apis/cate/type.ts b/frontend-pc/src/apis/cate/type.ts
new file mode 100644
index 0000000..8d97442
--- /dev/null
+++ b/frontend-pc/src/apis/cate/type.ts
@@ -0,0 +1,8 @@
+export interface CateTreeItem {
+ id: string
+ name: string
+ pid: string | null
+ type: string | null
+ disabled: boolean
+ children: CateTreeItem[]
+}
diff --git a/frontend-pc/src/apis/file/index.ts b/frontend-pc/src/apis/file/index.ts
new file mode 100644
index 0000000..31923bb
--- /dev/null
+++ b/frontend-pc/src/apis/file/index.ts
@@ -0,0 +1,9 @@
+import type * as T from './type'
+import http from '@/utils/http'
+
+export type * from './type'
+
+/** 获取文件列表 */
+export function getFileList(params: { fileType: string | number }) {
+ return http.get>('/file/getFileList', params)
+}
diff --git a/frontend-pc/src/apis/file/type.ts b/frontend-pc/src/apis/file/type.ts
new file mode 100644
index 0000000..fd58c7e
--- /dev/null
+++ b/frontend-pc/src/apis/file/type.ts
@@ -0,0 +1,13 @@
+// 文件
+export interface FileItem {
+ id: string
+ type: string
+ name: string
+ extendName: string
+ src: string
+ updateTime: string
+ isDir: boolean
+ filePath: string
+ size: number
+ [propName: string]: any // 一个 interface 中任意属性只能有一个
+}
diff --git a/frontend-pc/src/apis/person/index.ts b/frontend-pc/src/apis/person/index.ts
new file mode 100644
index 0000000..8b6959d
--- /dev/null
+++ b/frontend-pc/src/apis/person/index.ts
@@ -0,0 +1,7 @@
+import type * as T from './type'
+import { getBaseApi } from '@/apis/base'
+
+export type * from './type'
+
+/** 人物模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/person' })
diff --git a/frontend-pc/src/apis/person/type.ts b/frontend-pc/src/apis/person/type.ts
new file mode 100644
index 0000000..bbfae6b
--- /dev/null
+++ b/frontend-pc/src/apis/person/type.ts
@@ -0,0 +1,16 @@
+// 表格项目
+export interface ListItem {
+ id: string
+ name: string
+ account: string
+ avatar: string
+ gender: Gender
+ phone: string
+ email: string
+ createTime: string
+ address: string
+ age: number
+ status: Status
+ hobby: string[]
+ remark: string
+}
diff --git a/frontend-pc/src/apis/system/dept.ts b/frontend-pc/src/apis/system/dept.ts
new file mode 100644
index 0000000..edba807
--- /dev/null
+++ b/frontend-pc/src/apis/system/dept.ts
@@ -0,0 +1,15 @@
+import { getBaseApi } from '@/apis/base'
+
+export interface ListItem {
+ id: string
+ name: string
+ sort: number
+ status: Status
+ createTime: string
+ parentId: string
+ children?: ListItem[]
+ description: string
+}
+
+/** 部门模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/system/dept' })
diff --git a/frontend-pc/src/apis/system/dict.ts b/frontend-pc/src/apis/system/dict.ts
new file mode 100644
index 0000000..53fc692
--- /dev/null
+++ b/frontend-pc/src/apis/system/dict.ts
@@ -0,0 +1,44 @@
+import type { SelectOptionData } from '@arco-design/web-vue'
+import { getBaseApi } from '@/apis/base'
+import http from '@/utils/http'
+
+export interface ListItem {
+ id: string
+ createUserString: string
+ createTime: string
+ name: string
+ code: string
+ sort: number
+ status: Status
+ description: string
+}
+
+export type DictDetail = ListItem
+
+export type DictDataItem = {
+ id: string
+ name: string
+ value: string | number
+ sort: number
+ status: Status
+ remark?: string
+ createTime?: string
+}
+
+/** 字典模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/system/dict' })
+
+/** 字典数据列表 */
+export function getDictDataList(params: { code: string, name?: string, status?: string } & Pagination) {
+ return http.get>('/system/dict/getDictDataList', params)
+}
+
+/** 字典数据详情 */
+export function getDictDataDetail(params: { id: string, code: string }) {
+ return http.get('/system/dict/getDictDataDetail', params)
+}
+
+/** 获取字典数据映射 */
+export function getDictData() {
+ return http.get>('/system/dict/getDictData')
+}
diff --git a/frontend-pc/src/apis/system/menu.ts b/frontend-pc/src/apis/system/menu.ts
new file mode 100644
index 0000000..689e3f6
--- /dev/null
+++ b/frontend-pc/src/apis/system/menu.ts
@@ -0,0 +1,39 @@
+import { getBaseApi } from '@/apis/base'
+import http from '@/utils/http'
+
+export interface ListItem {
+ activeMenu: string
+ alwaysShow: boolean
+ breadcrumb: boolean
+ children: ListItem[]
+ component: string
+ hidden: boolean
+ icon: string
+ id: string
+ keepAlive: boolean
+ parentId: string
+ path: string
+ permission: string
+ redirect: string
+ roles: string[]
+ showInTabs: boolean
+ sort: number
+ status: Status
+ title: string
+ type: 1 | 2 | 3
+ affix: boolean
+}
+
+export interface MenuOptionsItem {
+ id: string
+ title: string
+ children: MenuOptionsItem[]
+}
+
+/** 菜单模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/system/menu' })
+
+/** 获取角色分配权限菜单树 */
+export function getMenuOptions() {
+ return http.get('/system/menu/getMenuOptions')
+}
diff --git a/frontend-pc/src/apis/system/role.ts b/frontend-pc/src/apis/system/role.ts
new file mode 100644
index 0000000..730d497
--- /dev/null
+++ b/frontend-pc/src/apis/system/role.ts
@@ -0,0 +1,23 @@
+import { getBaseApi } from '@/apis/base'
+import http from '@/utils/http'
+
+export interface ListItem {
+ id: string
+ createUserString: string
+ createTime: string
+ disabled: boolean
+ name: string
+ code: string
+ sort: number
+ status: Status
+ type: number
+ description: string
+}
+
+/** 角色模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/system/role' })
+
+/** 获取角色权限 */
+export function getRoleMenuIds(params: { role: string }) {
+ return http.get('/system/role/getRoleMenuIds', params)
+}
diff --git a/frontend-pc/src/apis/system/user.ts b/frontend-pc/src/apis/system/user.ts
new file mode 100644
index 0000000..1ec6607
--- /dev/null
+++ b/frontend-pc/src/apis/system/user.ts
@@ -0,0 +1,24 @@
+import { getBaseApi } from '@/apis/base'
+
+export interface ListItem {
+ id: string
+ createUserString: string
+ createTime: string
+ disabled: boolean
+ deptId: string
+ deptName: string
+ username: string
+ nickname: string
+ gender: Gender
+ avatar: string
+ email: string
+ phone: string
+ status: Status
+ type: 1 | 2
+ description: string
+ roleIds: string[]
+ roleNames: string[]
+}
+
+/** 用户模块 */
+export const baseAPI = getBaseApi({ baseUrl: '/system/user' })
diff --git a/frontend-pc/src/apis/test/index.ts b/frontend-pc/src/apis/test/index.ts
new file mode 100644
index 0000000..48b3a94
--- /dev/null
+++ b/frontend-pc/src/apis/test/index.ts
@@ -0,0 +1,11 @@
+import http from '@/utils/http'
+
+/** 请求成功的接口 */
+export function getTestSuccess() {
+ return http.get('/test/success')
+}
+
+/** 请求失败的接口 */
+export function getTestFail() {
+ return http.get('/test/fail')
+}
diff --git a/frontend-pc/src/apis/user/index.ts b/frontend-pc/src/apis/user/index.ts
new file mode 100644
index 0000000..ae6bd68
--- /dev/null
+++ b/frontend-pc/src/apis/user/index.ts
@@ -0,0 +1,24 @@
+import type * as T from './type'
+import http from '@/utils/http'
+
+export type * from './type'
+
+/** 登录 */
+export function login(data: { username: string, password: string }) {
+ return http.post('/user/login', data)
+}
+
+/** 退出登录 */
+export function logout() {
+ return http.post('/user/logout')
+}
+
+/** 获取用户信息 */
+export const getUserInfo = () => {
+ return http.get('/user/getUserInfo')
+}
+
+/** 获取用户路由信息 */
+export const getUserRoutes = () => {
+ return http.get('/user/getUserRoutes')
+}
diff --git a/frontend-pc/src/apis/user/type.ts b/frontend-pc/src/apis/user/type.ts
new file mode 100644
index 0000000..387384a
--- /dev/null
+++ b/frontend-pc/src/apis/user/type.ts
@@ -0,0 +1,34 @@
+export interface UserInfo {
+ id: string
+ nickname: string
+ avatar: string
+ roles: string[]
+ permissions: string[]
+}
+
+export interface Login {
+ token: string
+}
+
+export interface UserRouteItem {
+ activeMenu: string
+ alwaysShow: boolean
+ breadcrumb: boolean
+ children: UserRouteItem[]
+ component: string
+ hidden: boolean
+ icon: string
+ id: string
+ keepAlive: boolean
+ parentId: string
+ path: string
+ permission: string
+ redirect: string
+ roles: string[]
+ showInTabs: boolean
+ sort: number
+ status: Status
+ title: string
+ type: 1 | 2 | 3
+ affix: boolean
+}
diff --git a/frontend-pc/src/apis/xt/index.ts b/frontend-pc/src/apis/xt/index.ts
new file mode 100644
index 0000000..cefd9cc
--- /dev/null
+++ b/frontend-pc/src/apis/xt/index.ts
@@ -0,0 +1,81 @@
+/**
+ * 本项目业务接口的公共出口与类型。
+ *
+ * 与模板的 `src/apis/*` 分开,是因为两边契约不同:
+ * - 模板:`/user/login`、`{ success, message }`、`{ records, total }`
+ * - 本项目:`/auth/login`(《03-接口设计》§5)、`{ code, msg, data }`、`{ list, total }`
+ * 形状适配统一在 `@/utils/http` 里做,这里只负责路径与类型。
+ */
+import http from '@/utils/http'
+
+export { http }
+
+/** 登录接口(§5.1) */
+export interface LoginReq {
+ username: string
+ /** RSA 公钥加密后的 Base64,不是明文 */
+ password: string
+ /** 账号在多个租户下重名时必填 */
+ tenantCode?: string
+ captchaKey?: string
+ captchaCode?: string
+}
+
+export interface XtUserInfo {
+ id: number
+ username: string
+ realName: string
+ userType: number
+ avatarUrl: string | null
+ orgId: number | null
+}
+
+export interface XtTenantInfo {
+ id: number
+ tenantCode: string
+ tenantName: string
+ logoUrl: string | null
+}
+
+/** 登录 / 当前用户响应(§5.1 / §5.2) */
+export interface LoginVO {
+ token: string | null
+ expireSeconds: number
+ user: XtUserInfo
+ tenant: XtTenantInfo | null
+ roleCodes: string[]
+ /** 菜单键(决定左侧导航) */
+ menus: string[]
+ /** 细粒度权限码(决定按钮显隐) */
+ permissions: string[]
+}
+
+/** 图形验证码 */
+export interface CaptchaVO {
+ captchaKey: string
+ /** data:image/png;base64,... */
+ image: string
+ expireSeconds: number
+}
+
+// ── 认证(§5.1 / §5.2 / §5.9)──────────────────────────
+
+/** 登录(密码需先经 {@link encryptPassword} 加密) */
+export function login(data: LoginReq) {
+ return http.post('/auth/login', data)
+}
+
+/** 当前用户 + 菜单 + 权限码 */
+export function getMe() {
+ return http.get('/auth/me')
+}
+
+/** 登出(服务端会把 token 加入黑名单) */
+export function logout() {
+ return http.post('/auth/logout')
+}
+
+/** 图形验证码(连续失败 2 次后服务端会要求) */
+export function getCaptcha() {
+ return http.get('/auth/captcha')
+}
diff --git a/frontend-pc/src/apis/xt/org.ts b/frontend-pc/src/apis/xt/org.ts
new file mode 100644
index 0000000..accbdc5
--- /dev/null
+++ b/frontend-pc/src/apis/xt/org.ts
@@ -0,0 +1,63 @@
+/**
+ * 组织架构接口(《03-接口设计》§6.1~§6.3)。
+ *
+ * 契约差异(已在 `@/utils/http` 统一适配):后端 `{ code, msg, data }`,
+ * 分页 `{ list, total }`(适配层会补一份 `records`)。
+ */
+import http from '@/utils/http'
+
+/** 组织树节点(§6.1) */
+export interface OrgTreeVO {
+ id: number
+ orgName: string
+ /** 1 学校 / 2 院系或专业 / 3 班级 / 4 报名点 */
+ orgType: number
+ orgCode: string | null
+ leaderUserId: number | null
+ leaderName: string | null
+ sortNo: number
+ status: number
+ studentCount: number | null
+ children: OrgTreeVO[]
+}
+
+/** 新增 / 修改请求(§6.2) */
+export interface OrgUpsertReq {
+ parentId?: number | null
+ orgName: string
+ orgType: number
+ orgCode: string
+ leaderUserId?: number | null
+ sortNo?: number
+}
+
+/**
+ * 组织树。
+ * @param withCount 是否附带每节点学生数(会多一次聚合查询,列表页按需开)
+ */
+export function getOrgTree(params: { withCount?: boolean, rootId?: number } = {}) {
+ return http.get('/orgs/tree', params)
+}
+
+/** 新增,返回新节点 id */
+export function addOrg(data: OrgUpsertReq) {
+ return http.post('/orgs', data)
+}
+
+/** 修改;变更层级时服务端会自动重算整棵子树的 ancestor_path */
+export function updateOrg(id: number, data: OrgUpsertReq) {
+ return http.put(`/orgs/${id}`, data)
+}
+
+/** 删除;有下级组织或账号时返回 40015 并给出占用清单 */
+export function deleteOrg(id: number) {
+ return http.delete(`/orgs/${id}`)
+}
+
+/** 组织类型 → 中文名(与 DDL 的 org_type 对齐) */
+export const ORG_TYPE_NAMES: Record = {
+ 1: '学校',
+ 2: '院系 / 专业',
+ 3: '班级',
+ 4: '报名点'
+}
diff --git a/frontend-pc/src/apis/xt/tenant.ts b/frontend-pc/src/apis/xt/tenant.ts
new file mode 100644
index 0000000..e9ee085
--- /dev/null
+++ b/frontend-pc/src/apis/xt/tenant.ts
@@ -0,0 +1,83 @@
+/**
+ * 租户管理接口(《03-接口设计》§6.5~§6.6)。
+ *
+ * ⚠️ 全部接口**仅平台运营可用**。学校管理员调用会得到 40301,
+ * 前端据此隐藏入口(见 `@/config/xt-routes` 的菜单过滤)。
+ */
+import http from '@/utils/http'
+
+/** 租户(§6.5) */
+export interface TenantVO {
+ id: number
+ tenantCode: string
+ tenantName: string
+ tenantType: number
+ contactName: string | null
+ contactPhone: string | null
+ packageId: number | null
+ expireTime: string | null
+ status: number
+ logoUrl: string | null
+ createTime: string | null
+}
+
+/** 开通租户请求(§6.6) */
+export interface TenantCreateReq {
+ tenantName: string
+ tenantCode: string
+ tenantType: number
+ contactName: string
+ contactPhone: string
+ packageId?: number
+ expireTime: string
+ adminUsername: string
+ adminRealName: string
+ adminPhone: string
+}
+
+/** 开通结果:初始密码**只返回这一次** */
+export interface TenantCreateResult {
+ id: number
+ adminInitialPassword: string
+ miniProgramScene: string
+}
+
+/** 分页查询参数(后端用 pageNum/pageSize,模板的 useTable 用 page/size) */
+export interface TenantQuery {
+ keyword?: string
+ status?: number
+ expireBefore?: string
+ pageNum?: number
+ pageSize?: number
+}
+
+export function getTenantList(params: TenantQuery = {}) {
+ return http.get<{ list: TenantVO[], total: number }>('/tenants', params)
+}
+
+export function getTenantDetail(id: number) {
+ return http.get(`/tenants/${id}`)
+}
+
+/**
+ * 开通租户:一次请求完成「建租户 + 发放 6 个内置角色 + 创建首个管理员」。
+ * 返回的 `adminInitialPassword` 只在这一次响应里,界面必须让用户当场记下。
+ */
+export function createTenant(data: TenantCreateReq) {
+ return http.post('/tenants', data)
+}
+
+/** 租户类型 → 中文名 */
+export const TENANT_TYPE_NAMES: Record = {
+ 1: '学校',
+ 2: '认定机构',
+ 3: '报名点',
+ 4: '企业'
+}
+
+/** 租户状态 → 文案与颜色 */
+export const TENANT_STATUS: Record = {
+ 0: { text: '已停用', color: 'gray' },
+ 1: { text: '正常', color: 'green' },
+ 2: { text: '试用', color: 'orange' }
+}
diff --git a/frontend-pc/src/assets/images/login-hero.svg b/frontend-pc/src/assets/images/login-hero.svg
new file mode 100644
index 0000000..d383a3b
--- /dev/null
+++ b/frontend-pc/src/assets/images/login-hero.svg
@@ -0,0 +1,77 @@
+
diff --git a/frontend-pc/src/assets/images/logo.svg b/frontend-pc/src/assets/images/logo.svg
new file mode 100644
index 0000000..9f239d6
--- /dev/null
+++ b/frontend-pc/src/assets/images/logo.svg
@@ -0,0 +1,45 @@
+
diff --git a/frontend-pc/src/auto-import.d.ts b/frontend-pc/src/auto-import.d.ts
new file mode 100644
index 0000000..72370df
--- /dev/null
+++ b/frontend-pc/src/auto-import.d.ts
@@ -0,0 +1,78 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+// biome-ignore lint: disable
+export {}
+declare global {
+ const EffectScope: typeof import('vue').EffectScope
+ const computed: typeof import('vue').computed
+ const createApp: typeof import('vue').createApp
+ const customRef: typeof import('vue').customRef
+ const defineAsyncComponent: typeof import('vue').defineAsyncComponent
+ const defineComponent: typeof import('vue').defineComponent
+ const effectScope: typeof import('vue').effectScope
+ const getCurrentInstance: typeof import('vue').getCurrentInstance
+ const getCurrentScope: typeof import('vue').getCurrentScope
+ const getCurrentWatcher: typeof import('vue').getCurrentWatcher
+ const h: typeof import('vue').h
+ const inject: typeof import('vue').inject
+ const isProxy: typeof import('vue').isProxy
+ const isReactive: typeof import('vue').isReactive
+ const isReadonly: typeof import('vue').isReadonly
+ const isRef: typeof import('vue').isRef
+ const isShallow: typeof import('vue').isShallow
+ const markRaw: typeof import('vue').markRaw
+ const nextTick: typeof import('vue').nextTick
+ const onActivated: typeof import('vue').onActivated
+ const onBeforeMount: typeof import('vue').onBeforeMount
+ const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
+ const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
+ const onBeforeUnmount: typeof import('vue').onBeforeUnmount
+ const onBeforeUpdate: typeof import('vue').onBeforeUpdate
+ const onDeactivated: typeof import('vue').onDeactivated
+ const onErrorCaptured: typeof import('vue').onErrorCaptured
+ const onMounted: typeof import('vue').onMounted
+ const onRenderTracked: typeof import('vue').onRenderTracked
+ const onRenderTriggered: typeof import('vue').onRenderTriggered
+ const onScopeDispose: typeof import('vue').onScopeDispose
+ const onServerPrefetch: typeof import('vue').onServerPrefetch
+ const onUnmounted: typeof import('vue').onUnmounted
+ const onUpdated: typeof import('vue').onUpdated
+ const onWatcherCleanup: typeof import('vue').onWatcherCleanup
+ const provide: typeof import('vue').provide
+ const reactive: typeof import('vue').reactive
+ const readonly: typeof import('vue').readonly
+ const ref: typeof import('vue').ref
+ const resolveComponent: typeof import('vue').resolveComponent
+ const shallowReactive: typeof import('vue').shallowReactive
+ const shallowReadonly: typeof import('vue').shallowReadonly
+ const shallowRef: typeof import('vue').shallowRef
+ const toRaw: typeof import('vue').toRaw
+ const toRef: typeof import('vue').toRef
+ const toRefs: typeof import('vue').toRefs
+ const toValue: typeof import('vue').toValue
+ const triggerRef: typeof import('vue').triggerRef
+ const unref: typeof import('vue').unref
+ const useAttrs: typeof import('vue').useAttrs
+ const useCssModule: typeof import('vue').useCssModule
+ const useCssVars: typeof import('vue').useCssVars
+ const useId: typeof import('vue').useId
+ const useLink: typeof import('vue-router').useLink
+ const useModel: typeof import('vue').useModel
+ const useRoute: typeof import('vue-router').useRoute
+ const useRouter: typeof import('vue-router').useRouter
+ const useSlots: typeof import('vue').useSlots
+ const useTemplateRef: typeof import('vue').useTemplateRef
+ const watch: typeof import('vue').watch
+ const watchEffect: typeof import('vue').watchEffect
+ const watchPostEffect: typeof import('vue').watchPostEffect
+ const watchSyncEffect: typeof import('vue').watchSyncEffect
+}
+// for type re-export
+declare global {
+ // @ts-ignore
+ export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
+ import('vue')
+}
diff --git a/frontend-pc/src/components.d.ts b/frontend-pc/src/components.d.ts
new file mode 100644
index 0000000..029eb77
--- /dev/null
+++ b/frontend-pc/src/components.d.ts
@@ -0,0 +1,160 @@
+/* eslint-disable */
+// @ts-nocheck
+// biome-ignore lint: disable
+// oxlint-disable
+// ------
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import { GlobalComponents } from 'vue'
+
+export {}
+
+/* prettier-ignore */
+declare module 'vue' {
+ export interface GlobalComponents {
+ AAlert: typeof import('@arco-design/web-vue')['Alert']
+ AAvatar: typeof import('@arco-design/web-vue')['Avatar']
+ ABadge: typeof import('@arco-design/web-vue')['Badge']
+ ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
+ ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
+ AButton: typeof import('@arco-design/web-vue')['Button']
+ ACard: typeof import('@arco-design/web-vue')['Card']
+ ACol: typeof import('@arco-design/web-vue')['Col']
+ AComment: typeof import('@arco-design/web-vue')['Comment']
+ AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
+ ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
+ ADescriptions: typeof import('@arco-design/web-vue')['Descriptions']
+ ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem']
+ ADivider: typeof import('@arco-design/web-vue')['Divider']
+ ADoption: typeof import('@arco-design/web-vue')['Doption']
+ ADrawer: typeof import('@arco-design/web-vue')['Drawer']
+ ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
+ AEmpty: typeof import('@arco-design/web-vue')['Empty']
+ AForm: typeof import('@arco-design/web-vue')['Form']
+ AFormItem: typeof import('@arco-design/web-vue')['FormItem']
+ AInput: typeof import('@arco-design/web-vue')['Input']
+ AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
+ AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
+ AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
+ ALayout: typeof import('@arco-design/web-vue')['Layout']
+ ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
+ ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
+ ALink: typeof import('@arco-design/web-vue')['Link']
+ AMenu: typeof import('@arco-design/web-vue')['Menu']
+ AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
+ AModal: typeof import('@arco-design/web-vue')['Modal']
+ AOption: typeof import('@arco-design/web-vue')['Option']
+ APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm']
+ APopover: typeof import('@arco-design/web-vue')['Popover']
+ ARow: typeof import('@arco-design/web-vue')['Row']
+ AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
+ ASelect: typeof import('@arco-design/web-vue')['Select']
+ ASpace: typeof import('@arco-design/web-vue')['Space']
+ ASpin: typeof import('@arco-design/web-vue')['Spin']
+ ASplit: typeof import('@arco-design/web-vue')['Split']
+ ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
+ ASwitch: typeof import('@arco-design/web-vue')['Switch']
+ ATable: typeof import('@arco-design/web-vue')['Table']
+ ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
+ ATabPane: typeof import('@arco-design/web-vue')['TabPane']
+ ATabs: typeof import('@arco-design/web-vue')['Tabs']
+ ATag: typeof import('@arco-design/web-vue')['Tag']
+ ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
+ ATree: typeof import('@arco-design/web-vue')['Tree']
+ GiButton: typeof import('./components/GiButton/GiButton.vue')['default']
+ GiCellAvatar: typeof import('./components/GiCell/GiCellAvatar.vue')['default']
+ GiCellGender: typeof import('./components/GiCell/GiCellGender.vue')['default']
+ GiCellStatus: typeof import('./components/GiCell/GiCellStatus.vue')['default']
+ GiCellTags: typeof import('./components/GiCell/GiCellTags.vue')['default']
+ GiDot: typeof import('./components/GiDot/GiDot.tsx')['default']
+ GiEditTable: typeof import('./components/GiEditTable/GiEditTable.vue')['default']
+ GiFlex: typeof import('./components/GiFlex/GiFlex.vue')['default']
+ GiForm: typeof import('./components/GiForm/GiForm.vue')['default']
+ GiIconBox: typeof import('./components/GiIconBox/GiIconBox.vue')['default']
+ GiInputSearch: typeof import('./components/GiInputSearch/GiInputSearch.vue')['default']
+ GiOption: typeof import('./components/GiOption/GiOption.vue')['default']
+ GiOptionItem: typeof import('./components/GiOption/GiOptionItem.vue')['default']
+ GiPageLayout: typeof import('./components/GiPageLayout/GiPageLayout.vue')['default']
+ GiTable: typeof import('./components/GiTable/GiTable.vue')['default']
+ GiTableSetting: typeof import('./components/GiTableSetting/GiTableSetting.vue')['default']
+ GiTag: typeof import('./components/GiTag/GiTag.tsx')['default']
+ GiThemeBtn: typeof import('./components/GiThemeBtn/GiThemeBtn.vue')['default']
+ RouterLink: typeof import('vue-router')['RouterLink']
+ RouterView: typeof import('vue-router')['RouterView']
+ SplitButton: typeof import('./components/GiPageLayout/SplitButton.vue')['default']
+ }
+}
+
+// For TSX support
+declare global {
+ const AAlert: typeof import('@arco-design/web-vue')['Alert']
+ const AAvatar: typeof import('@arco-design/web-vue')['Avatar']
+ const ABadge: typeof import('@arco-design/web-vue')['Badge']
+ const ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
+ const ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
+ const AButton: typeof import('@arco-design/web-vue')['Button']
+ const ACard: typeof import('@arco-design/web-vue')['Card']
+ const ACol: typeof import('@arco-design/web-vue')['Col']
+ const AComment: typeof import('@arco-design/web-vue')['Comment']
+ const AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
+ const ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
+ const ADescriptions: typeof import('@arco-design/web-vue')['Descriptions']
+ const ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem']
+ const ADivider: typeof import('@arco-design/web-vue')['Divider']
+ const ADoption: typeof import('@arco-design/web-vue')['Doption']
+ const ADrawer: typeof import('@arco-design/web-vue')['Drawer']
+ const ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
+ const AEmpty: typeof import('@arco-design/web-vue')['Empty']
+ const AForm: typeof import('@arco-design/web-vue')['Form']
+ const AFormItem: typeof import('@arco-design/web-vue')['FormItem']
+ const AInput: typeof import('@arco-design/web-vue')['Input']
+ const AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
+ const AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
+ const AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
+ const ALayout: typeof import('@arco-design/web-vue')['Layout']
+ const ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
+ const ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
+ const ALink: typeof import('@arco-design/web-vue')['Link']
+ const AMenu: typeof import('@arco-design/web-vue')['Menu']
+ const AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
+ const AModal: typeof import('@arco-design/web-vue')['Modal']
+ const AOption: typeof import('@arco-design/web-vue')['Option']
+ const APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm']
+ const APopover: typeof import('@arco-design/web-vue')['Popover']
+ const ARow: typeof import('@arco-design/web-vue')['Row']
+ const AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
+ const ASelect: typeof import('@arco-design/web-vue')['Select']
+ const ASpace: typeof import('@arco-design/web-vue')['Space']
+ const ASpin: typeof import('@arco-design/web-vue')['Spin']
+ const ASplit: typeof import('@arco-design/web-vue')['Split']
+ const ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
+ const ASwitch: typeof import('@arco-design/web-vue')['Switch']
+ const ATable: typeof import('@arco-design/web-vue')['Table']
+ const ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
+ const ATabPane: typeof import('@arco-design/web-vue')['TabPane']
+ const ATabs: typeof import('@arco-design/web-vue')['Tabs']
+ const ATag: typeof import('@arco-design/web-vue')['Tag']
+ const ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
+ const ATree: typeof import('@arco-design/web-vue')['Tree']
+ const GiButton: typeof import('./components/GiButton/GiButton.vue')['default']
+ const GiCellAvatar: typeof import('./components/GiCell/GiCellAvatar.vue')['default']
+ const GiCellGender: typeof import('./components/GiCell/GiCellGender.vue')['default']
+ const GiCellStatus: typeof import('./components/GiCell/GiCellStatus.vue')['default']
+ const GiCellTags: typeof import('./components/GiCell/GiCellTags.vue')['default']
+ const GiDot: typeof import('./components/GiDot/GiDot.tsx')['default']
+ const GiEditTable: typeof import('./components/GiEditTable/GiEditTable.vue')['default']
+ const GiFlex: typeof import('./components/GiFlex/GiFlex.vue')['default']
+ const GiForm: typeof import('./components/GiForm/GiForm.vue')['default']
+ const GiIconBox: typeof import('./components/GiIconBox/GiIconBox.vue')['default']
+ const GiInputSearch: typeof import('./components/GiInputSearch/GiInputSearch.vue')['default']
+ const GiOption: typeof import('./components/GiOption/GiOption.vue')['default']
+ const GiOptionItem: typeof import('./components/GiOption/GiOptionItem.vue')['default']
+ const GiPageLayout: typeof import('./components/GiPageLayout/GiPageLayout.vue')['default']
+ const GiTable: typeof import('./components/GiTable/GiTable.vue')['default']
+ const GiTableSetting: typeof import('./components/GiTableSetting/GiTableSetting.vue')['default']
+ const GiTag: typeof import('./components/GiTag/GiTag.tsx')['default']
+ const GiThemeBtn: typeof import('./components/GiThemeBtn/GiThemeBtn.vue')['default']
+ const RouterLink: typeof import('vue-router')['RouterLink']
+ const RouterView: typeof import('vue-router')['RouterView']
+ const SplitButton: typeof import('./components/GiPageLayout/SplitButton.vue')['default']
+}
\ No newline at end of file
diff --git a/frontend-pc/src/components/GiButton/GiButton.vue b/frontend-pc/src/components/GiButton/GiButton.vue
new file mode 100644
index 0000000..637f1f7
--- /dev/null
+++ b/frontend-pc/src/components/GiButton/GiButton.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+ {{ buttonConfig?.text }}
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiButton/index.ts b/frontend-pc/src/components/GiButton/index.ts
new file mode 100644
index 0000000..9750fe6
--- /dev/null
+++ b/frontend-pc/src/components/GiButton/index.ts
@@ -0,0 +1,5 @@
+import Button from './GiButton.vue'
+
+export type ButtonInstance = InstanceType
+export * from './type'
+export { Button as GiButton }
diff --git a/frontend-pc/src/components/GiButton/type.ts b/frontend-pc/src/components/GiButton/type.ts
new file mode 100644
index 0000000..7c6bf35
--- /dev/null
+++ b/frontend-pc/src/components/GiButton/type.ts
@@ -0,0 +1,5 @@
+import type { ButtonProps as ArcoButtonProps } from '@arco-design/web-vue'
+
+export interface ButtonProps extends Omit {
+ type?: 'add' | 'edit' | 'delete' | 'search' | 'reset' | 'upload' | 'import' | 'export' | ''
+}
diff --git a/frontend-pc/src/components/GiCell/GiCellAvatar.vue b/frontend-pc/src/components/GiCell/GiCellAvatar.vue
new file mode 100644
index 0000000..5bf95dd
--- /dev/null
+++ b/frontend-pc/src/components/GiCell/GiCellAvatar.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+ {{ props.name }}
+ {{ props.name }}
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiCell/GiCellGender.vue b/frontend-pc/src/components/GiCell/GiCellGender.vue
new file mode 100644
index 0000000..ee22cbf
--- /dev/null
+++ b/frontend-pc/src/components/GiCell/GiCellGender.vue
@@ -0,0 +1,28 @@
+
+
+
+ 男
+
+
+
+ 女
+
+
+
+ 保密
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiCell/GiCellStatus.vue b/frontend-pc/src/components/GiCell/GiCellStatus.vue
new file mode 100644
index 0000000..918885e
--- /dev/null
+++ b/frontend-pc/src/components/GiCell/GiCellStatus.vue
@@ -0,0 +1,24 @@
+
+
+
+ 正常
+
+
+
+ 禁用
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiCell/GiCellTags.vue b/frontend-pc/src/components/GiCell/GiCellTags.vue
new file mode 100644
index 0000000..7456aab
--- /dev/null
+++ b/frontend-pc/src/components/GiCell/GiCellTags.vue
@@ -0,0 +1,33 @@
+
+
+
+ {{ item }}
+
+
+
+ +{{ number }}
+
+
+
+ {{ tag }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiCell/index.ts b/frontend-pc/src/components/GiCell/index.ts
new file mode 100644
index 0000000..d39267f
--- /dev/null
+++ b/frontend-pc/src/components/GiCell/index.ts
@@ -0,0 +1,6 @@
+import GiCellAvatar from './GiCellAvatar.vue'
+import GiCellGender from './GiCellGender.vue'
+import GiCellStatus from './GiCellStatus.vue'
+import GiCellTags from './GiCellTags.vue'
+
+export { GiCellAvatar, GiCellGender, GiCellStatus, GiCellTags }
diff --git a/frontend-pc/src/components/GiDot/GiDot.tsx b/frontend-pc/src/components/GiDot/GiDot.tsx
new file mode 100644
index 0000000..83c6119
--- /dev/null
+++ b/frontend-pc/src/components/GiDot/GiDot.tsx
@@ -0,0 +1,33 @@
+/**
+ * @file GiDot 组件 - 用于显示带有可选动画效果的状态点
+ */
+import type { PropType } from 'vue'
+import type { DotType, DotProps as Props } from './type'
+import { defineComponent } from 'vue'
+import './dot.scss'
+
+export default defineComponent({
+ name: 'GiDot',
+ props: {
+ animation: {
+ type: Boolean as PropType,
+ default: true,
+ required: false
+ },
+ type: {
+ type: String as PropType,
+ default: 'primary',
+ required: false,
+ validator: (value: string): boolean => {
+ return ['primary', 'success', 'warning', 'danger', 'info'].includes(value)
+ }
+ }
+ },
+ setup(props: Props) {
+ return () => (
+
+ )
+ }
+})
diff --git a/frontend-pc/src/components/GiDot/dot.scss b/frontend-pc/src/components/GiDot/dot.scss
new file mode 100644
index 0000000..a762721
--- /dev/null
+++ b/frontend-pc/src/components/GiDot/dot.scss
@@ -0,0 +1,58 @@
+.gi-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ vertical-align: middle;
+ background: #000;
+ border-radius: 50%;
+}
+
+$types: primary, success, warning, danger, info;
+
+@each $i in $types {
+ .gi-dot-#{$i}
+
+ {
+ background-color: rgb(var(--#{$i}-6));
+ }
+
+ @if ($i ==info) {
+ .gi-dot-#{$i}
+
+ {
+ background-color: rgb(var(--gray-6));
+ }
+ }
+}
+
+.gi-dot-processing {
+ position: relative;
+}
+
+.gi-dot-processing::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ content: '';
+ background: inherit;
+ border-radius: 50%;
+ animation: gi-dot-animated 1.2s ease-in-out infinite;
+}
+
+@keyframes gi-dot-animated {
+ 0% {
+ opacity: 1;
+ transform: scale(0.5);
+ }
+
+ 30% {
+ opacity: 0.7;
+ }
+
+ 100% {
+ opacity: 0;
+ transform: scale(2.5);
+ }
+}
\ No newline at end of file
diff --git a/frontend-pc/src/components/GiDot/index.ts b/frontend-pc/src/components/GiDot/index.ts
new file mode 100644
index 0000000..0f39877
--- /dev/null
+++ b/frontend-pc/src/components/GiDot/index.ts
@@ -0,0 +1,5 @@
+import Dot from './GiDot.tsx'
+
+export type * from './type'
+export type DotInstance = InstanceType
+export { Dot as GiDot }
diff --git a/frontend-pc/src/components/GiDot/type.ts b/frontend-pc/src/components/GiDot/type.ts
new file mode 100644
index 0000000..c066c69
--- /dev/null
+++ b/frontend-pc/src/components/GiDot/type.ts
@@ -0,0 +1,10 @@
+/** 点的类型 */
+export type DotType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
+
+/** 组件属性定义 */
+export interface DotProps {
+ /** 点的类型 */
+ type?: DotType
+ /** 是否启用动画效果 */
+ animation?: boolean
+}
diff --git a/frontend-pc/src/components/GiEditTable/GiEditTable.vue b/frontend-pc/src/components/GiEditTable/GiEditTable.vue
new file mode 100644
index 0000000..f6bef61
--- /dev/null
+++ b/frontend-pc/src/components/GiEditTable/GiEditTable.vue
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+ {{ col?.columnProps?.title || col.title }}
+
+
+ {{ record[col.dataIndex] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiEditTable/index.ts b/frontend-pc/src/components/GiEditTable/index.ts
new file mode 100644
index 0000000..9a599be
--- /dev/null
+++ b/frontend-pc/src/components/GiEditTable/index.ts
@@ -0,0 +1,5 @@
+import EditTable from './GiEditTable.vue'
+
+export type * from './type'
+export type EditTableInstance = InstanceType
+export { EditTable as GiEditTable }
diff --git a/frontend-pc/src/components/GiEditTable/type.ts b/frontend-pc/src/components/GiEditTable/type.ts
new file mode 100644
index 0000000..02fc519
--- /dev/null
+++ b/frontend-pc/src/components/GiEditTable/type.ts
@@ -0,0 +1,81 @@
+import type * as A from '@arco-design/web-vue'
+import type { VNode } from 'vue'
+
+export type EditTableColumnItemType
+ = | 'input'
+ | 'input-number'
+ | 'input-tag'
+ | 'textarea'
+ | 'select'
+ | 'tree-select'
+ | 'radio'
+ | 'radio-group'
+ | 'checkbox'
+ | 'checkbox-group'
+ | 'date-picker'
+ | 'year-picker'
+ | 'quarter-picker'
+ | 'month-picker'
+ | 'week-picker'
+ | 'time-picker'
+ | 'range-picker'
+ | 'color-picker'
+ | 'rate'
+ | 'switch'
+ | 'slider'
+ | 'cascader'
+ | 'upload'
+ | 'auto-complete'
+ | 'mention'
+ | ''
+
+export type EditTableComponentProps
+ = & A.InputInstance['$props']
+ & A.InputNumberInstance['$props']
+ & A.InputTagInstance['$props']
+ & A.TextareaInstance['$props']
+ & A.SelectInstance['$props']
+ & A.TreeSelectInstance['$props']
+ & A.RadioInstance['$props']
+ & A.RadioGroupInstance['$props']
+ & A.CheckboxInstance['$props']
+ & A.CheckboxGroupInstance['$props']
+ & A.DatePickerInstance['$props']
+ & A.YearPickerInstance['$props']
+ & A.QuarterPickerInstance['$props']
+ & A.MonthPickerInstance['$props']
+ & A.WeekPickerInstance['$props']
+ & A.TimePickerInstance['$props']
+ & A.RangePickerInstance['$props']
+ & A.ColorPickerInstance['$props']
+ & A.RateInstance['$props']
+ & A.SwitchInstance['$props']
+ & A.SliderInstance['$props']
+ & A.CascaderInstance['$props']
+ & A.UploadInstance['$props']
+ & A.AutoCompleteInstance['$props']
+ & A.MentionInstance['$props']
+
+interface EditTableColumnItemProps extends Partial> {
+ placeholder?: string | string[]
+}
+
+export interface EditTableColumnItem {
+ type?: EditTableColumnItemType
+ title: string
+ dataIndex: string
+ required?: boolean
+ rules?: A.FormItemInstance['$props']['rules'] // 表单校验规则
+ props?: EditTableColumnItemProps
+ columnProps?: Partial> & { title?: string | (() => VNode) }
+ formItemProps?: A.FormItemInstance['$props']
+ slotName?: string
+}
+
+export type EditTableCellDisabled = boolean | ((e: { row: T, rowIndex: number, col: EditTableColumnItem }) => boolean)
+
+export interface EditTableProps {
+ columns: EditTableColumnItem[]
+ data: T[]
+ cellDisabled?: EditTableCellDisabled
+}
diff --git a/frontend-pc/src/components/GiFlex/GiFlex.vue b/frontend-pc/src/components/GiFlex/GiFlex.vue
new file mode 100644
index 0000000..b418f81
--- /dev/null
+++ b/frontend-pc/src/components/GiFlex/GiFlex.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiFlex/index.ts b/frontend-pc/src/components/GiFlex/index.ts
new file mode 100644
index 0000000..4293675
--- /dev/null
+++ b/frontend-pc/src/components/GiFlex/index.ts
@@ -0,0 +1,5 @@
+import Flex from './GiFlex.vue'
+
+export type FlexInstance = InstanceType
+export * from './type'
+export { Flex as GiFlex }
diff --git a/frontend-pc/src/components/GiFlex/type.ts b/frontend-pc/src/components/GiFlex/type.ts
new file mode 100644
index 0000000..715fd2f
--- /dev/null
+++ b/frontend-pc/src/components/GiFlex/type.ts
@@ -0,0 +1,29 @@
+/** 组件属性定义 */
+export interface FlexProps {
+ /** 排列方向 */
+ column?: boolean
+ /** 水平对齐方式(与 CSS justify-content 一致) */
+ justify?:
+ | 'start'
+ | 'center'
+ | 'end'
+ | 'flex-start'
+ | 'flex-end'
+ | 'space-between'
+ | 'space-around'
+ | 'space-evenly'
+ /** 垂直对齐方式(与 CSS align-items 一致) */
+ align?:
+ | 'start'
+ | 'center'
+ | 'end'
+ | 'flex-start'
+ | 'flex-end'
+ | 'baseline'
+ | 'stretch'
+ /** 是否自动换行 */
+ wrap?: boolean
+ /** `boolean` 须写在联合类型最前,否则模板里单独写 `gap` 会被解析成 `""` 而不是 `true` */
+ gap?: boolean | number | string
+ flex?: number | string
+}
diff --git a/frontend-pc/src/components/GiForm/GiForm.vue b/frontend-pc/src/components/GiForm/GiForm.vue
new file mode 100644
index 0000000..b3d3462
--- /dev/null
+++ b/frontend-pc/src/components/GiForm/GiForm.vue
@@ -0,0 +1,273 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+ {{ slotValue }}
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+ {{ slotValue }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.searchBtnText }}
+
+ 重置
+
+
+
+
+
+ {{ collapsed ? '展开' : '收起' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiForm/index.ts b/frontend-pc/src/components/GiForm/index.ts
new file mode 100644
index 0000000..219e98e
--- /dev/null
+++ b/frontend-pc/src/components/GiForm/index.ts
@@ -0,0 +1,5 @@
+import Form from './GiForm.vue'
+
+export type * from './type'
+export type FormInstance = InstanceType
+export { Form as GiForm }
diff --git a/frontend-pc/src/components/GiForm/type.ts b/frontend-pc/src/components/GiForm/type.ts
new file mode 100644
index 0000000..c37bc0b
--- /dev/null
+++ b/frontend-pc/src/components/GiForm/type.ts
@@ -0,0 +1,299 @@
+/**
+ * @file GiForm 组件类型定义
+ * @description 包含表单组件所需的所有类型定义,用于构建动态表单
+ */
+
+import type * as A from '@arco-design/web-vue'
+import type { GridItemProps } from '@arco-design/web-vue'
+import type { Component, VNode } from 'vue'
+import type { MergeMultiple } from '@/types/tool'
+
+type FormItemProps = A.FormItemInstance['$props']
+
+/** 支持的表单项类型 */
+export type FormColumnItemType
+ = | 'input' // 输入框
+ | 'input-number' // 数字输入框
+ | 'input-tag' // 标签输入框
+ | 'input-search' // 搜索框
+ | 'textarea' // 文本域
+ | 'select' // 选择器
+ | 'tree-select' // 树选择器
+ | 'radio' // 单选框
+ | 'radio-group' // 单选框组
+ | 'checkbox' // 复选框
+ | 'checkbox-group' // 复选框组
+ | 'date-picker' // 日期选择器
+ | 'year-picker' // 年份选择器
+ | 'quarter-picker' // 季度选择器
+ | 'month-picker' // 月份选择器
+ | 'week-picker' // 周选择器
+ | 'time-picker' // 时间选择器
+ | 'range-picker' // 范围选择器
+ | 'color-picker' // 颜色选择器
+ | 'rate' // 评分
+ | 'switch' // 开关
+ | 'slider' // 滑块
+ | 'cascader' // 级联选择器
+ | 'upload' // 上传
+ | 'auto-complete' // 自动完成
+ | 'mention' // 提及
+ | 'group-title' // 分组标题
+ | 'slot' // 插槽,由父组件通过具名插槽渲染内容
+ | Component
+
+/** 组件属性合并类型 */
+export type FormComponentProps = MergeMultiple<[
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial,
+ Partial
+]>
+
+/** 选项类型定义 */
+export type FormColumnItemOptions
+ = MergeMultiple<[A.SelectInstance['$props']['options'], A.RadioGroupInstance['$props']['options'], A.CheckboxGroupInstance['$props']['options'], A.CascaderInstance['$props']['options']]>
+
+/** 数据源类型定义 */
+export type FormColumnItemData
+ = MergeMultiple<[A.TreeSelectInstance['$props']['data'], A.AutoCompleteInstance['$props']['data'], A.MentionInstance['$props']['data']]>
+
+/** 自动完成组件插槽 */
+type AutoCompleteSlots = {
+ option: (e: { data: (string | number | A.SelectOptionData | A.SelectOptionGroup)[] }) => VNode
+ footer: () => VNode
+}
+
+/** 级联选择器插槽 */
+type CascaderSlots = {
+ 'label': (e: { data: A.CascaderOption }) => VNode
+ 'prefix': () => VNode
+ 'arrow-icon': () => VNode
+ 'loading-icon': () => VNode
+ 'search-icon': () => VNode
+ 'empty': () => VNode
+ 'option': (e: { data: A.CascaderOption }) => VNode
+}
+
+/** 复选框组插槽 */
+type CheckboxGroupSlots = {
+ checkbox: (e: { checked: boolean, disabled: string }) => VNode
+ label: (e: { data: A.CheckboxOption }) => VNode
+}
+
+/** 单选框组插槽 */
+type RadioGroupSlots = {
+ radio: (e: { checked: boolean, disabled: string }) => VNode
+ label: (e: { data: any }) => VNode
+}
+
+/** 日期选择器插槽 */
+type DatePickerSlots = {
+ 'prefix': () => VNode
+ 'suffix-icon': () => VNode
+ 'icon-next-double': () => VNode
+ 'icon-prev-double': () => VNode
+ 'icon-next': () => VNode
+ 'icon-prev': () => VNode
+ 'cell': (e: { data: Date }) => VNode
+ 'extra': () => VNode
+}
+
+/** 输入框插槽 */
+type InputSlots = {
+ append: (() => VNode) | string
+ prepend: (() => VNode) | string
+ suffix: (() => VNode) | string
+ prefix: (() => VNode) | string
+}
+
+/** 数字输入框插槽 */
+type InputNumberSlots = {
+ minus: (() => VNode) | string
+ plus: (() => VNode) | string
+ append: (() => VNode) | string
+ prepend: (() => VNode) | string
+ suffix: (() => VNode) | string
+}
+
+/** 标签输入框插槽 */
+type InputTagSlots = {
+ tag: (e: { data: A.TagData }) => VNode
+ prefix: (() => VNode) | string
+ suffix: (() => VNode) | string
+}
+
+/** 评分插槽 */
+type RateSlots = {
+ character: (e: { index: number }) => VNode
+}
+
+/** 选择器插槽 */
+type SelectSlots = {
+ 'trigger': () => VNode
+ 'prefix': () => VNode
+ 'search-icon': () => VNode
+ 'loading-icon': () => VNode
+ 'arrow-icon': () => VNode
+ 'footer': () => VNode
+ 'header': () => VNode
+ 'label': (e: { data: A.SelectOptionData }) => VNode
+ 'option': (e: { data: A.SelectOptionData }) => VNode
+ 'empty': () => VNode
+}
+
+/** 开关插槽 */
+type SwitchSlots = {
+ 'checked-icon': () => VNode
+ 'unchecked-icon': () => VNode
+ 'checked': () => VNode
+ 'unchecked': () => VNode
+}
+
+/** 树选择器插槽 */
+type TreeSelectSlots = {
+ 'trigger': () => VNode
+ 'prefix': () => VNode
+ 'label': (e: { data: any }) => VNode
+ 'header': () => VNode
+ 'loader': () => VNode
+ 'empty': () => VNode
+ 'footer': () => VNode
+ 'tree-slot-extra': () => VNode
+ 'tree-slot-title': (e: { title: string }) => VNode
+ 'tree-slot-icon': (e: { node: A.TreeNodeData }) => VNode
+ 'tree-slot-switcher-icon': () => VNode
+}
+
+/** 提及插槽 */
+type MentionSlots = {
+ option: (e: { data: any }) => VNode
+}
+
+/** 组件插槽合并类型 */
+export type FormComponentSlots = MergeMultiple<[
+ AutoCompleteSlots,
+ CascaderSlots,
+ CheckboxGroupSlots,
+ RadioGroupSlots,
+ DatePickerSlots,
+ InputSlots,
+ InputNumberSlots,
+ InputTagSlots,
+ RateSlots,
+ SelectSlots,
+ SwitchSlots,
+ TreeSelectSlots,
+ MentionSlots
+]>
+
+/** 表单项插槽接口 */
+export type FormColumnItemSlots = FormComponentSlots
+
+/** 表单项显隐控制类型 */
+export type FormColumnItemHide = boolean | ((form: F) => boolean)
+export type FormColumnItemDisabled = boolean | ((form: F) => boolean)
+
+/** 表单项数据请求相关类型 */
+export type FormColumnItemRequest = (form: F) => Promise
+export type FormColumnItemFormat = (res: T) => FormColumnItemOptions | FormColumnItemData
+
+/** 表单列配置项接口 */
+export interface FormColumnItem {
+ /** 表单项类型 */
+ type?: FormColumnItemType
+ /** 标签名 */
+ label?: string
+ /** 字段名 */
+ field: string
+ fieldName?: string
+ /** 自定义渲染label */
+ labelRender?: (() => VNode)
+ /** 表单项属性 */
+ props?: Partial
+ /** 表单项校验规则 */
+ rules?: FormItemProps['rules']
+ /** 是否必填 */
+ required?: boolean
+ /** 栅格属性 */
+ gridItemProps?: GridItemProps
+ /** 表单项属性 */
+ formItemProps?: FormItemProps
+ /** 表单项插槽 */
+ slots?: FormColumnItemSlots
+ /** 表单项组件插槽 */
+ formItemSlots?: Record VNode)>
+ /** 是否隐藏(支持函数) */
+ hide?: boolean | ((model: F) => boolean)
+ /** 是否禁用(支持函数) */
+ disabled?: boolean | ((model: F) => boolean)
+ /** 栅格跨度 */
+ span?: number | Record
+}
+
+/** Props 类型定义 */
+export interface FormProps {
+ /** 表单数据对象 */
+ modelValue: any
+ /** 表单布局方式 */
+ layout?: A.FormInstance['layout']
+ /** 表单尺寸 */
+ size?: A.FormInstance['size']
+ /** 标签布局属性 */
+ labelColProps?: A.FormInstance['labelColProps']
+ /** 表单项布局属性 */
+ wrapperColProps?: A.FormInstance['wrapperColProps']
+ /** 标签对齐方式 */
+ labelAlign?: A.FormInstance['labelAlign']
+ /** 是否禁用表单 */
+ disabled?: A.FormInstance['disabled']
+ /** 表单校验规则 */
+ rules?: A.FormInstance['rules']
+ /** 自动标签宽度 */
+ autoLabelWidth?: A.FormInstance['autoLabelWidth']
+ /** 表单ID */
+ id?: A.FormInstance['id']
+ /** 是否滚动到第一个错误项 */
+ scrollToFirstError?: A.FormInstance['scrollToFirstError']
+ /** 表单列配置 */
+ columns: FormColumnItem[]
+ /** 栅格布局属性 */
+ gridProps?: A.GridProps
+ /** 栅格项属性 */
+ gridItemProps?: GridItemProps
+ /** 是否为搜索模式 */
+ search?: boolean
+ /** 默认是否折叠 */
+ defaultCollapsed?: boolean
+ /** 搜索按钮文字 */
+ searchBtnText?: string
+ /** 是否隐藏折叠按钮 */
+ hideFoldBtn?: boolean
+ /** 是否显示后缀 */
+ suffix?: boolean
+ /** 表单控制 */
+ fc?: Record
+}
diff --git a/frontend-pc/src/components/GiIconBox/GiIconBox.vue b/frontend-pc/src/components/GiIconBox/GiIconBox.vue
new file mode 100644
index 0000000..2d184bb
--- /dev/null
+++ b/frontend-pc/src/components/GiIconBox/GiIconBox.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiIconBox/index.ts b/frontend-pc/src/components/GiIconBox/index.ts
new file mode 100644
index 0000000..255bffc
--- /dev/null
+++ b/frontend-pc/src/components/GiIconBox/index.ts
@@ -0,0 +1,5 @@
+import IconBox from './GiIconBox.vue'
+
+export * from './type'
+export type IconBoxInstance = InstanceType
+export { IconBox as GiIconBox }
diff --git a/frontend-pc/src/components/GiIconBox/type.ts b/frontend-pc/src/components/GiIconBox/type.ts
new file mode 100644
index 0000000..180d504
--- /dev/null
+++ b/frontend-pc/src/components/GiIconBox/type.ts
@@ -0,0 +1,6 @@
+/** 组件属性定义 */
+export interface IconBoxProps {
+ color?: 'primary' | 'success' | 'warning' | 'danger' | string
+ size?: number
+ shape?: 'square' | 'round'
+}
diff --git a/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue b/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue
new file mode 100644
index 0000000..fbdf1e5
--- /dev/null
+++ b/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiInputSearch/index.ts b/frontend-pc/src/components/GiInputSearch/index.ts
new file mode 100644
index 0000000..9e3fb86
--- /dev/null
+++ b/frontend-pc/src/components/GiInputSearch/index.ts
@@ -0,0 +1,5 @@
+import InputSearch from './GiInputSearch.vue'
+
+export type InputSearchInstance = InstanceType
+export * from './type'
+export { InputSearch as GiInputSearch }
diff --git a/frontend-pc/src/components/GiInputSearch/type.ts b/frontend-pc/src/components/GiInputSearch/type.ts
new file mode 100644
index 0000000..b6cbf1c
--- /dev/null
+++ b/frontend-pc/src/components/GiInputSearch/type.ts
@@ -0,0 +1,5 @@
+export interface InputSearchProps {
+ placeholder?: string
+ disabled?: boolean
+ readonly?: boolean
+}
diff --git a/frontend-pc/src/components/GiOption/GiOption.vue b/frontend-pc/src/components/GiOption/GiOption.vue
new file mode 100644
index 0000000..8a35cb8
--- /dev/null
+++ b/frontend-pc/src/components/GiOption/GiOption.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiOption/GiOptionItem.vue b/frontend-pc/src/components/GiOption/GiOptionItem.vue
new file mode 100644
index 0000000..72f9550
--- /dev/null
+++ b/frontend-pc/src/components/GiOption/GiOptionItem.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+ {{ props.label }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiOption/index.ts b/frontend-pc/src/components/GiOption/index.ts
new file mode 100644
index 0000000..811323f
--- /dev/null
+++ b/frontend-pc/src/components/GiOption/index.ts
@@ -0,0 +1,7 @@
+import Option from './GiOption.vue'
+import OptionItem from './GiOptionItem.vue'
+
+export type OptionInstance = InstanceType
+export type OptionItemInstance = InstanceType
+export * from './type'
+export { Option as GiOption, OptionItem as GiOptionItem }
diff --git a/frontend-pc/src/components/GiOption/type.ts b/frontend-pc/src/components/GiOption/type.ts
new file mode 100644
index 0000000..bbddcce
--- /dev/null
+++ b/frontend-pc/src/components/GiOption/type.ts
@@ -0,0 +1,11 @@
+/** 组件属性定义 */
+export interface OptionItemProps {
+ /** 图标名称 */
+ icon?: string
+ /** 选项文本 */
+ label?: string
+ /** 是否显示更多图标 */
+ more?: boolean
+ /** 是否激活状态 */
+ active?: boolean
+}
diff --git a/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue b/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue
new file mode 100644
index 0000000..f2511cb
--- /dev/null
+++ b/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiPageLayout/SplitButton.vue b/frontend-pc/src/components/GiPageLayout/SplitButton.vue
new file mode 100644
index 0000000..820ab64
--- /dev/null
+++ b/frontend-pc/src/components/GiPageLayout/SplitButton.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiPageLayout/index.ts b/frontend-pc/src/components/GiPageLayout/index.ts
new file mode 100644
index 0000000..7050a1b
--- /dev/null
+++ b/frontend-pc/src/components/GiPageLayout/index.ts
@@ -0,0 +1,5 @@
+import PageLayout from './GiPageLayout.vue'
+
+export type PageLayoutInstance = InstanceType
+export * from './type'
+export { PageLayout as GiPageLayout }
diff --git a/frontend-pc/src/components/GiPageLayout/type.ts b/frontend-pc/src/components/GiPageLayout/type.ts
new file mode 100644
index 0000000..e25e74e
--- /dev/null
+++ b/frontend-pc/src/components/GiPageLayout/type.ts
@@ -0,0 +1,14 @@
+import type { CSSProperties } from 'vue'
+
+/** 组件属性定义 */
+export interface PageLayoutProps {
+ size?: string
+ margin?: boolean
+ inner?: boolean
+ headerBordered?: boolean
+ leftStyle?: CSSProperties
+ headerStyle?: CSSProperties
+ bodyStyle?: CSSProperties
+ collapsed?: boolean
+ bgTransparent?: boolean
+}
diff --git a/frontend-pc/src/components/GiTable/GiTable.vue b/frontend-pc/src/components/GiTable/GiTable.vue
new file mode 100644
index 0000000..5efa51b
--- /dev/null
+++ b/frontend-pc/src/components/GiTable/GiTable.vue
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+ {{ props.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{ settingColumnList = val as TableSettingColumnItem[] }">
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiTable/index.ts b/frontend-pc/src/components/GiTable/index.ts
new file mode 100644
index 0000000..eeee32b
--- /dev/null
+++ b/frontend-pc/src/components/GiTable/index.ts
@@ -0,0 +1,5 @@
+import Table from './GiTable.vue'
+
+export type * from './type'
+export type TableInstance = InstanceType
+export { Table as GiTable }
diff --git a/frontend-pc/src/components/GiTable/type.ts b/frontend-pc/src/components/GiTable/type.ts
new file mode 100644
index 0000000..359a266
--- /dev/null
+++ b/frontend-pc/src/components/GiTable/type.ts
@@ -0,0 +1,53 @@
+import type { TableInstance } from '@arco-design/web-vue'
+
+export type TableProps = {
+ columns?: TableInstance['$props']['columns']
+ data?: TableInstance['$props']['data']
+ bordered?: TableInstance['$props']['bordered']
+ hoverable?: TableInstance['$props']['hoverable']
+ stripe?: TableInstance['$props']['stripe']
+ size?: TableInstance['$props']['size']
+ tableLayoutFixed?: TableInstance['$props']['tableLayoutFixed']
+ loading?: TableInstance['$props']['loading']
+ rowSelection?: TableInstance['$props']['rowSelection']
+ expandable?: TableInstance['$props']['expandable']
+ scroll?: TableInstance['$props']['scroll']
+ pagination?: TableInstance['$props']['pagination']
+ pagePosition?: TableInstance['$props']['pagePosition']
+ indentSize?: TableInstance['$props']['indentSize']
+ rowKey?: TableInstance['$props']['rowKey']
+ showHeader?: TableInstance['$props']['showHeader']
+ virtualListProps?: TableInstance['$props']['virtualListProps']
+ spanMethod?: TableInstance['$props']['spanMethod']
+ spanAll?: TableInstance['$props']['spanAll']
+ loadMore?: TableInstance['$props']['loadMore']
+ filterIconAlignLeft?: TableInstance['$props']['filterIconAlignLeft']
+ hideExpandButtonOnEmpty?: TableInstance['$props']['hideExpandButtonOnEmpty']
+ rowClass?: TableInstance['$props']['rowClass']
+ draggable?: TableInstance['$props']['draggable']
+ rowNumber?: TableInstance['$props']['rowNumber']
+ columnResizable?: TableInstance['$props']['columnResizable']
+ summary?: TableInstance['$props']['summary']
+ summaryText?: TableInstance['$props']['summaryText']
+ summarySpanMethod?: TableInstance['$props']['summarySpanMethod']
+ selectedKeys?: TableInstance['$props']['selectedKeys']
+ defaultSelectedKeys?: TableInstance['$props']['defaultSelectedKeys']
+ expandedKeys?: TableInstance['$props']['expandedKeys']
+ defaultExpandedKeys?: TableInstance['$props']['defaultExpandedKeys']
+ defaultExpandAllRows?: TableInstance['$props']['defaultExpandAllRows']
+ stickyHeader?: TableInstance['$props']['stickyHeader']
+ scrollbar?: TableInstance['$props']['scrollbar']
+ showEmptyTree?: TableInstance['$props']['showEmptyTree']
+}
+
+/** 列设置项类型 */
+export interface TableSettingColumnItem {
+ title: string
+ key: string
+ show: boolean
+ disabled: boolean
+ /** 固定左侧 */
+ fixedLeft?: boolean
+ /** 固定右侧 */
+ fixedRight?: boolean
+}
diff --git a/frontend-pc/src/components/GiTableSetting/GiTableSetting.vue b/frontend-pc/src/components/GiTableSetting/GiTableSetting.vue
new file mode 100644
index 0000000..acebb7c
--- /dev/null
+++ b/frontend-pc/src/components/GiTableSetting/GiTableSetting.vue
@@ -0,0 +1,291 @@
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiTableSetting/index.ts b/frontend-pc/src/components/GiTableSetting/index.ts
new file mode 100644
index 0000000..048b334
--- /dev/null
+++ b/frontend-pc/src/components/GiTableSetting/index.ts
@@ -0,0 +1,5 @@
+import TableSetting from './GiTableSetting.vue'
+
+export type * from './type'
+export type TableSettingInstance = InstanceType
+export { TableSetting as GiTableSetting }
diff --git a/frontend-pc/src/components/GiTableSetting/type.ts b/frontend-pc/src/components/GiTableSetting/type.ts
new file mode 100644
index 0000000..53eb8f7
--- /dev/null
+++ b/frontend-pc/src/components/GiTableSetting/type.ts
@@ -0,0 +1,22 @@
+import type { TableInstance } from '@arco-design/web-vue'
+
+export interface GiTableSettingProps {
+ /** 工具栏标题 */
+ title?: string
+ /** 列配置(用于列设置) */
+ columns?: TableInstance['columns']
+ /** 禁止控制显示隐藏的列 key */
+ disabledColumnKeys?: string[]
+}
+
+/** 列设置项类型 */
+export interface TableSettingColumnListItem {
+ title: string
+ key: string
+ show: boolean
+ disabled: boolean
+ /** 固定左侧 */
+ fixedLeft?: boolean
+ /** 固定右侧 */
+ fixedRight?: boolean
+}
diff --git a/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts b/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts
new file mode 100644
index 0000000..5fab867
--- /dev/null
+++ b/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts
@@ -0,0 +1,141 @@
+import type { TableColumnData, TableInstance } from '@arco-design/web-vue'
+import type { MaybeRefOrGetter } from 'vue'
+import type { TableSettingColumnListItem } from './type'
+import { computed, ref, toValue, watch } from 'vue'
+
+export interface UseTableColumnSettingOptions {
+ columns: MaybeRefOrGetter
+ disabledColumnKeys?: MaybeRefOrGetter
+}
+
+/** 获取列的 key */
+export function getTableColumnKey(column: TableColumnData, index?: number) {
+ if (column.dataIndex) return String(column.dataIndex)
+ if (typeof column.title === 'string' && column.title) return column.title
+ return `__column_${index ?? Date.now()}__`
+}
+
+/** 表格列设置:显隐、排序、固定 */
+export function useTableColumnSetting(options: UseTableColumnSettingOptions) {
+ const showSettingColumnBtn = computed(() => {
+ const columns = Array.isArray(toValue(options.columns)) ? toValue(options.columns) : undefined
+ return Boolean(columns?.length)
+ })
+
+ const initialSettingColumns = computed(() => {
+ const raw = toValue(options.columns)
+ if (!raw) return []
+
+ const columns = Array.isArray(raw) ? raw : []
+ return columns.map((column, index) => {
+ const key = getTableColumnKey(column, index)
+ const fixed = column.fixed
+ const disabledKeys = toValue(options.disabledColumnKeys) ?? []
+ return {
+ key,
+ title: typeof column.title === 'string' ? column.title : '',
+ show: true,
+ disabled: disabledKeys.includes(key),
+ fixedLeft: fixed === 'left',
+ fixedRight: fixed === 'right'
+ }
+ })
+ })
+
+ const settingColumnList = ref([])
+
+ const isColumnStructureMatch = (
+ userColumns: TableSettingColumnListItem[],
+ initialColumns: TableSettingColumnListItem[]
+ ): boolean => {
+ if (userColumns.length === 0 || userColumns.length !== initialColumns.length) return false
+ const initialKeys = new Set(initialColumns.map((item) => item.key))
+ const userKeys = new Set(userColumns.map((item) => item.key))
+ return initialKeys.size === userKeys.size && [...initialKeys].every((key) => userKeys.has(key))
+ }
+
+ const columnMap = computed(() => {
+ const raw = toValue(options.columns)
+ if (!raw) return new Map()
+ const columns = Array.isArray(raw) ? raw : []
+ return new Map(columns.map((col, index) => [getTableColumnKey(col, index), col]))
+ })
+
+ const currentSettingColumns = computed(() => {
+ const isValid = isColumnStructureMatch(settingColumnList.value, initialSettingColumns.value)
+ return isValid ? settingColumnList.value : initialSettingColumns.value
+ })
+
+ const resetSettingColumns = () => {
+ settingColumnList.value = [...initialSettingColumns.value]
+ }
+
+ const ensureSettingColumnList = () => {
+ if (settingColumnList.value.length === 0 && initialSettingColumns.value.length > 0) {
+ settingColumnList.value = initialSettingColumns.value.map((item) => ({ ...item }))
+ }
+ }
+
+ const toggleFixedLeft = (key: string) => {
+ ensureSettingColumnList()
+ settingColumnList.value = settingColumnList.value.map((item) =>
+ item.key === key
+ ? { ...item, fixedLeft: !item.fixedLeft, fixedRight: false }
+ : item
+ )
+ }
+
+ const toggleFixedRight = (key: string) => {
+ ensureSettingColumnList()
+ settingColumnList.value = settingColumnList.value.map((item) =>
+ item.key === key
+ ? { ...item, fixedRight: !item.fixedRight, fixedLeft: false }
+ : item
+ )
+ }
+
+ watch(
+ () => toValue(options.columns),
+ () => {
+ if (!isColumnStructureMatch(settingColumnList.value, initialSettingColumns.value)) {
+ resetSettingColumns()
+ }
+ },
+ { deep: true }
+ )
+
+ const settingColumns = computed(() => {
+ const raw = toValue(options.columns)
+ if (!raw) return []
+
+ const shown = currentSettingColumns.value.filter((item) => item.show)
+ const leftFixed: typeof shown = []
+ const noFixed: typeof shown = []
+ const rightFixed: typeof shown = []
+ for (const item of shown) {
+ if (item.fixedLeft) leftFixed.push(item)
+ else if (item.fixedRight) rightFixed.push(item)
+ else noFixed.push(item)
+ }
+ const ordered = [...leftFixed, ...noFixed, ...rightFixed]
+
+ return ordered
+ .map((item) => {
+ const col = columnMap.value.get(item.key)
+ if (!col) return null
+ const fixed = item.fixedRight ? 'right' : item.fixedLeft ? 'left' : undefined
+ return { ...col, fixed }
+ })
+ .filter(Boolean) as TableColumnData[]
+ })
+
+ return {
+ showSettingColumnBtn,
+ currentSettingColumns,
+ settingColumnList,
+ settingColumns,
+ resetSettingColumns,
+ toggleFixedLeft,
+ toggleFixedRight
+ }
+}
diff --git a/frontend-pc/src/components/GiTag/GiTag.tsx b/frontend-pc/src/components/GiTag/GiTag.tsx
new file mode 100644
index 0000000..07ae1a4
--- /dev/null
+++ b/frontend-pc/src/components/GiTag/GiTag.tsx
@@ -0,0 +1,135 @@
+/**
+ * @file GiTag 组件 - 提供多样化的标签展示
+ * @description 支持多种样式类型、状态、颜色和大小的标签组件
+ */
+
+import type { CSSProperties, PropType } from 'vue'
+import type { TagProps as Props } from './type'
+import { computed, defineComponent } from 'vue'
+import './tag.scss'
+
+/** 基础颜色映射表 */
+const BASE_COLORS: Record = {
+ red: '#FF0000',
+ orangered: '#f77234',
+ orange: '#ff7d00',
+ gold: '#f7ba1e',
+ lime: '#9fdb1d',
+ green: '#00b42a',
+ cyan: '#14c9c9',
+ blue: '#3491fa',
+ purple: '#722ed1',
+ pink: '#f5319d',
+ gray: '#86909c'
+} as const
+
+export default defineComponent({
+ name: 'GiTag',
+ props: {
+ type: {
+ type: String as PropType,
+ default: 'light'
+ },
+ status: {
+ type: String as PropType,
+ default: 'primary'
+ },
+ color: {
+ type: String as PropType,
+ default: ''
+ },
+ size: {
+ type: String as PropType,
+ default: 'small'
+ },
+ closable: {
+ type: Boolean,
+ default: false
+ }
+ },
+ emits: ['click', 'close'],
+ setup(props: Props, { slots, emit }) {
+ /**
+ * 将十六进制颜色转换为 RGB 对象
+ * @param hex - 十六进制颜色值
+ */
+ const hexToRgb = (hex: string) => {
+ const cleanHex = hex.startsWith('#') ? hex.slice(1) : hex
+ return {
+ r: Number.parseInt(cleanHex.slice(0, 2), 16),
+ g: Number.parseInt(cleanHex.slice(2, 4), 16),
+ b: Number.parseInt(cleanHex.slice(4, 6), 16)
+ }
+ }
+
+ /** 计算标签的 class */
+ const className = computed(() => {
+ return [
+ 'gi-tag',
+ props.type && `gi-tag__type--${props.type}`,
+ props.size && `gi-tag__size--${props.size}`,
+ props.status && `gi-tag__status--${props.status}`
+ ].filter(Boolean)
+ })
+
+ /** 计算标签的样式 */
+ const calcStyle = computed((): CSSProperties => {
+ if (!props.color) return {}
+
+ const color = BASE_COLORS[props.color as string] || props.color
+ const { r, g, b } = hexToRgb(color)
+ const style: CSSProperties = {}
+
+ switch (props.type) {
+ case 'light':
+ style.color = color
+ style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.1)`
+ style['--tag-close-hover-bg-color'] = color
+ break
+ case 'dark':
+ style.color = '#fff'
+ style.backgroundColor = color
+ style['--tag-close-hover-color'] = color
+ style['--tag-close-hover-bg-color'] = 'rgba(255, 255, 255, 0.9)'
+ break
+ case 'outline':
+ style.color = color
+ style.backgroundColor = 'transparent'
+ style.borderColor = color
+ style['--tag-close-hover-bg-color'] = color
+ break
+ case 'light-outline':
+ style.color = color
+ style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.1)`
+ style.borderColor = `rgba(${r}, ${g}, ${b}, 0.2)`
+ style['--tag-close-hover-bg-color'] = color
+ break
+ }
+
+ return style
+ })
+
+ /** 点击标签事件处理 */
+ const handleClick = () => emit('click')
+
+ /** 关闭标签事件处理 */
+ const handleClose = (event: MouseEvent) => {
+ event.stopPropagation()
+ emit('close')
+ }
+
+ /** 关闭图标组件 */
+ const CloseIcon = props.closable && (
+
+
+
+ )
+
+ return () => (
+
+ {slots.default?.()}
+ {CloseIcon}
+
+ )
+ }
+})
diff --git a/frontend-pc/src/components/GiTag/index.ts b/frontend-pc/src/components/GiTag/index.ts
new file mode 100644
index 0000000..fe51656
--- /dev/null
+++ b/frontend-pc/src/components/GiTag/index.ts
@@ -0,0 +1,5 @@
+import Tag from './GiTag.tsx'
+
+export type TagInstance = InstanceType
+export * from './type'
+export { Tag as GiTag }
diff --git a/frontend-pc/src/components/GiTag/tag.scss b/frontend-pc/src/components/GiTag/tag.scss
new file mode 100644
index 0000000..ed27c19
--- /dev/null
+++ b/frontend-pc/src/components/GiTag/tag.scss
@@ -0,0 +1,166 @@
+$status: primary, success, warning, danger;
+
+$tag-size-mini-height: 20px;
+$tag-size-small-height: 22px;
+$tag-size-large-height: 24px;
+
+$tag-size-mini-padding: 0 6px;
+$tag-size-small-padding: 0 8px;
+$tag-size-large-padding: 0 10px;
+
+.gi-tag {
+ box-sizing: border-box;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: $tag-size-small-height;
+ padding: $tag-size-small-padding;
+ font-size: 12px;
+ line-height: 1;
+ white-space: nowrap;
+ cursor: pointer;
+ border-radius: 3px;
+}
+
+.gi-tag-close-btn {
+ position: relative;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 16px;
+ height: 16px;
+ margin-left: 4px;
+ cursor: pointer;
+ background-color: transparent;
+ border-radius: var(--border-radius-circle);
+ transition: background-color 0.1s cubic-bezier(0, 0, 1, 1);
+
+ .close-icon {
+ z-index: 9;
+ width: 12px;
+ height: 12px;
+ }
+}
+
+.gi-tag__size--mini {
+ height: $tag-size-mini-height;
+ padding: $tag-size-mini-padding;
+
+ .gi-tag-close-btn {
+ .close-icon {
+ width: 10px;
+ height: 10px;
+ }
+
+ &::before {
+ width: 14px;
+ height: 14px;
+ }
+ }
+}
+
+.gi-tag__size--small {
+ height: $tag-size-small-height;
+ padding: $tag-size-small-padding;
+}
+
+.gi-tag__size--large {
+ height: $tag-size-large-height;
+ padding: $tag-size-small-padding;
+}
+
+.gi-tag__type--light {
+ color: #fff;
+
+ @each $i in $status {
+ &.gi-tag__status--#{$i}
+
+ {
+ color: rgb(var(--#{$i}-6));
+ background-color: rgb(var(--#{$i}-1));
+
+ --tag-close-hover-color: #fff;
+ --tag-close-hover-bg-color: rgb(var(--#{$i}-6));
+
+ .gi-tag-close-btn {
+ &:hover {
+ color: var(--tag-close-hover-color);
+ background-color: var(--tag-close-hover-bg-color);
+ }
+ }
+ }
+ }
+}
+
+.gi-tag__type--dark {
+ color: #fff;
+
+ @each $i in $status {
+ &.gi-tag__status--#{$i}
+
+ {
+ background-color: rgb(var(--#{$i}-6));
+
+ --tag-close-hover-color: rgb(var(--#{$i}-6));
+ --tag-close-hover-bg-color: rgb(255 255 255 / 90%);
+
+ .gi-tag-close-btn {
+ &:hover {
+ color: var(--tag-close-hover-color);
+ background-color: var(--tag-close-hover-bg-color);
+ }
+ }
+ }
+ }
+}
+
+.gi-tag__type--outline {
+ background: transparent;
+ border-style: solid;
+ border-width: 1px;
+
+ @each $i in $status {
+ &.gi-tag__status--#{$i}
+
+ {
+ color: rgb(var(--#{$i}-6));
+ border-color: rgb(var(--#{$i}-6));
+
+ --tag-close-hover-color: #fff;
+ --tag-close-hover-bg-color: rgb(var(--#{$i}-6));
+
+ .gi-tag-close-btn {
+ &:hover {
+ color: var(--tag-close-hover-color);
+ background-color: var(--tag-close-hover-bg-color);
+ }
+ }
+ }
+ }
+}
+
+.gi-tag__type--light-outline {
+ border-style: solid;
+ border-width: 1px;
+
+ @each $i in $status {
+ &.gi-tag__status--#{$i}
+
+ {
+ color: rgb(var(--#{$i}-6));
+ background-color: rgb(var(--#{$i}-1));
+ border-color: rgb(var(--#{$i}-2));
+
+ --tag-close-hover-color: #fff;
+ --tag-close-hover-bg-color: rgb(var(--#{$i}-6));
+
+ .gi-tag-close-btn {
+ &:hover {
+ color: var(--tag-close-hover-color);
+ background-color: var(--tag-close-hover-bg-color);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-pc/src/components/GiTag/type.ts b/frontend-pc/src/components/GiTag/type.ts
new file mode 100644
index 0000000..91e282e
--- /dev/null
+++ b/frontend-pc/src/components/GiTag/type.ts
@@ -0,0 +1,13 @@
+/** 组件属性定义 */
+export interface TagProps {
+ /** 标签类型 */
+ type?: 'dark' | 'light' | 'outline' | 'light-outline'
+ /** 标签状态 */
+ status?: 'primary' | 'success' | 'warning' | 'danger' | 'info'
+ /** 自定义颜色,支持预设颜色名或十六进制颜色值 */
+ color?: 'red' | 'orangered' | 'orange' | 'gold' | 'lime' | 'green' | 'cyan' | 'blue' | 'purple' | 'pink' | 'gray' | string
+ /** 标签尺寸 */
+ size?: 'mini' | 'small' | 'large'
+ /** 是否可关闭 */
+ closable?: boolean
+}
diff --git a/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue b/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue
new file mode 100644
index 0000000..19d2d05
--- /dev/null
+++ b/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/GiThemeBtn/index.ts b/frontend-pc/src/components/GiThemeBtn/index.ts
new file mode 100644
index 0000000..228ec31
--- /dev/null
+++ b/frontend-pc/src/components/GiThemeBtn/index.ts
@@ -0,0 +1,4 @@
+import ThemeBtn from './GiThemeBtn.vue'
+
+export type ThemeBtnInstance = InstanceType
+export { ThemeBtn as GiThemeBtn }
diff --git a/frontend-pc/src/components/dialog/TreeUserList/Tree.vue b/frontend-pc/src/components/dialog/TreeUserList/Tree.vue
new file mode 100644
index 0000000..3921133
--- /dev/null
+++ b/frontend-pc/src/components/dialog/TreeUserList/Tree.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/dialog/TreeUserList/index.vue b/frontend-pc/src/components/dialog/TreeUserList/index.vue
new file mode 100644
index 0000000..91f27d0
--- /dev/null
+++ b/frontend-pc/src/components/dialog/TreeUserList/index.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/dialog/UserList/index.vue b/frontend-pc/src/components/dialog/UserList/index.vue
new file mode 100644
index 0000000..9645e20
--- /dev/null
+++ b/frontend-pc/src/components/dialog/UserList/index.vue
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+ 查询
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/dialog/index.ts b/frontend-pc/src/components/dialog/index.ts
new file mode 100644
index 0000000..3d3ffd5
--- /dev/null
+++ b/frontend-pc/src/components/dialog/index.ts
@@ -0,0 +1,16 @@
+import type { ListItem as UserListItem } from '@/apis/person'
+import { createSelectDialog } from '@/utils/createSelectDialog'
+import TreeUserList from './TreeUserList/index.vue'
+import UserList from './UserList/index.vue'
+
+/** 选择用户列表的对话框 */
+export const SelectUserListDialog = createSelectDialog({
+ title: '选择用户',
+ component: UserList
+})
+
+/** 左树右表的对话框 */
+export const SelectTreeUserListDialog = createSelectDialog({
+ title: '左树右表对话框示例',
+ component: TreeUserList
+})
diff --git a/frontend-pc/src/components/icons/Icon403.vue b/frontend-pc/src/components/icons/Icon403.vue
new file mode 100644
index 0000000..6f0abf4
--- /dev/null
+++ b/frontend-pc/src/components/icons/Icon403.vue
@@ -0,0 +1,625 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/icons/Icon404.vue b/frontend-pc/src/components/icons/Icon404.vue
new file mode 100644
index 0000000..e6972fd
--- /dev/null
+++ b/frontend-pc/src/components/icons/Icon404.vue
@@ -0,0 +1,363 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/icons/Icon500.vue b/frontend-pc/src/components/icons/Icon500.vue
new file mode 100644
index 0000000..91b82a2
--- /dev/null
+++ b/frontend-pc/src/components/icons/Icon500.vue
@@ -0,0 +1,967 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/components/index.ts b/frontend-pc/src/components/index.ts
new file mode 100644
index 0000000..6ab124a
--- /dev/null
+++ b/frontend-pc/src/components/index.ts
@@ -0,0 +1,20 @@
+export * from './GiButton'
+export * from './GiCell'
+export * from './GiCodeButton'
+export * from './GiCodeView'
+export * from './GiDot'
+export * from './GiEditTable'
+export * from './GiFlex'
+export * from './GiFooter'
+export * from './GiForm'
+export * from './GiIconBox'
+export * from './GiIconSelector'
+export * from './GiIframe'
+export * from './GiInputSearch'
+export * from './GiOption'
+export * from './GiPageLayout'
+export * from './GiTable'
+export * from './GiTableSetting'
+export * from './GiTag'
+export * from './GiThemeBtn'
+export * from './GiTrend'
diff --git a/frontend-pc/src/config/setting.json b/frontend-pc/src/config/setting.json
new file mode 100644
index 0000000..c8cd449
--- /dev/null
+++ b/frontend-pc/src/config/setting.json
@@ -0,0 +1,11 @@
+{
+ "themeColor": "#0052D9",
+ "layout": "left",
+ "tabStyle": "card",
+ "isTabVisible": true,
+ "transitionName": "zoom-fade",
+ "isTransitionEnabled": true,
+ "isMenuCollapsed": false,
+ "isMenuAccordion": false,
+ "isMenuDark": false
+}
diff --git a/frontend-pc/src/config/xt-routes.ts b/frontend-pc/src/config/xt-routes.ts
new file mode 100644
index 0000000..5fcd568
--- /dev/null
+++ b/frontend-pc/src/config/xt-routes.ts
@@ -0,0 +1,230 @@
+/**
+ * 业务菜单与路由表。
+ *
+ * 设计:**这一张表同时驱动三件事** —— 左侧菜单、路由注册、权限提示。
+ * 三者若各写一份,改一处漏一处几乎必然发生(表现是"菜单点进去 404"或"有权限但不显示")。
+ *
+ * 菜单可见性由**后端**决定:`/auth/me` 返回 `menus: string[]`(菜单键集合),
+ * 这里按菜单键过滤。真正的访问控制也在后端(绕过前端只会拿到 40301),
+ * 前端的过滤只是"不显示没权限的入口"。
+ *
+ * ⚠️ `key` 必须与后端 `BuiltinRole` 里的菜单键逐字一致。
+ */
+import type { UserRouteItem } from '@/apis/user'
+
+/** 单个菜单定义 */
+export interface XtMenu {
+ /** 菜单键,与后端一致 */
+ key: string
+ title: string
+ /** iconify 图标名(icon-park-outline 集) */
+ icon: string
+ /** 路由路径(完整,以 / 开头) */
+ path: string
+ /** 视图路径(相对 src/views,不带扩展名);分组节点为空 */
+ component?: string
+ /** 分组标题;null 表示不分组 */
+ group: string | null
+ sort: number
+ /** 一期是否已实现。false 的走「待建」页,比直接隐藏更诚实 */
+ ready: boolean
+ /** 待建说明 */
+ note?: string
+}
+
+/** 分组顺序(决定左侧菜单顺序) */
+const GROUPS: Array<{ id: string, title: string, icon: string, sort: number }> = [
+ { id: 'collect', title: '收集管理', icon: 'icon-park-outline:application-two', sort: 10 },
+ { id: 'setting', title: '配置', icon: 'icon-park-outline:setting-two', sort: 20 },
+ { id: 'org', title: '组织与账号', icon: 'icon-park-outline:every-user', sort: 30 },
+ { id: 'platform', title: '平台运维', icon: 'icon-park-outline:server', sort: 40 }
+]
+
+/** 业务菜单(工作台在 baseRoutes 里,不列这里) */
+export const XT_MENUS: XtMenu[] = [
+ // ── 收集管理 ──────────────────────────────────────
+ {
+ key: 'task', title: '收集任务', icon: 'icon-park-outline:list-two',
+ path: '/collect/task', component: 'task/index', group: 'collect', sort: 11, ready: false,
+ note: '建任务、配表单与材料规则、发布与暂停。对应里程碑 M1。'
+ },
+ {
+ key: 'roster', title: '名单管理', icon: 'icon-park-outline:user-business',
+ path: '/collect/roster', component: 'roster/index', group: 'collect', sort: 12, ready: false,
+ note: '导入名册、报考范围确认、门禁名单维护。对应里程碑 M2。'
+ },
+ {
+ key: 'join', title: '加入申请', icon: 'icon-park-outline:user-to-user-transmission',
+ path: '/collect/join', component: 'join/index', group: 'collect', sort: 13, ready: false,
+ note: '门禁未匹配的人提交加入申请,管理员在此审批。对应里程碑 M2。'
+ },
+ {
+ key: 'audit', title: '审核', icon: 'icon-park-outline:check-one',
+ path: '/collect/audit', component: 'audit/index', group: 'collect', sort: 14, ready: false,
+ note: '班主任初审(右滑通过)、管理员终审、批量操作。对应里程碑 M5。'
+ },
+ {
+ key: 'export', title: '导出中心', icon: 'icon-park-outline:download-two',
+ path: '/collect/export', component: 'export/index', group: 'collect', sort: 15, ready: false,
+ note: '按目录规范打包汇总,不合格文件禁止进包。对应里程碑 M6。'
+ },
+
+ // ── 配置 ─────────────────────────────────────────
+ {
+ key: 'form', title: '表单设计器', icon: 'icon-park-outline:form-one',
+ path: '/setting/form', component: 'form/index', group: 'setting', sort: 21, ready: false,
+ note: '内置字段锁定、扩展字段自由。对应里程碑 M1。'
+ },
+ {
+ key: 'material', title: '材料规则', icon: 'icon-park-outline:file-editing',
+ path: '/setting/material', component: 'material/index', group: 'setting', sort: 22, ready: false,
+ note: '命名模板、别名、格式白名单、体积上限、压缩档位。对应里程碑 M1。'
+ },
+ {
+ key: 'cleanup', title: '历史整理器', icon: 'icon-park-outline:magic-wand',
+ path: '/setting/cleanup', component: 'cleanup/index', group: 'setting', sort: 23, ready: false,
+ note: '把已有的乱文件夹/压缩包批量规范化并出体检报告。对应里程碑 M3。'
+ },
+
+ // ── 组织与账号 ────────────────────────────────────
+ {
+ key: 'org', title: '组织架构', icon: 'icon-park-outline:tree-list',
+ path: '/org/manage', component: 'org/index', group: 'org', sort: 31, ready: true
+ },
+ {
+ key: 'user', title: '账号管理', icon: 'icon-park-outline:peoples',
+ path: '/org/user', component: 'user/index', group: 'org', sort: 32, ready: false,
+ note: '账号增删改查与 Excel 批量导入(含体检报告)。属 M0 剩余任务。'
+ },
+ {
+ key: 'role', title: '角色与授权', icon: 'icon-park-outline:permissions',
+ path: '/org/role', component: 'role/index', group: 'org', sort: 33, ready: false,
+ note: '内置角色的权限由代码决定、不可改;自定义角色属二期。'
+ },
+
+ // ── 平台运维 ──────────────────────────────────────
+ {
+ key: 'tenant', title: '租户管理', icon: 'icon-park-outline:school',
+ path: '/platform/tenant', component: 'tenant/index', group: 'platform', sort: 41, ready: true
+ },
+ {
+ key: 'log', title: '操作日志', icon: 'icon-park-outline:log',
+ path: '/platform/log', component: 'log/index', group: 'platform', sort: 42, ready: false,
+ note: '登录、查看明文、下载、导出、代传、删除的审计记录。'
+ }
+]
+
+/** 构造一个「待建」路由项(组件统一指向 coming-soon) */
+function placeholder(menu: XtMenu): UserRouteItem {
+ return {
+ id: menu.key,
+ parentId: '0',
+ path: menu.path,
+ component: 'coming-soon/index',
+ title: menu.title,
+ icon: menu.icon,
+ sort: menu.sort,
+ type: 2,
+ status: '1',
+ hidden: false,
+ keepAlive: false,
+ affix: false,
+ breadcrumb: true,
+ showInTabs: true,
+ alwaysShow: false,
+ activeMenu: '',
+ permission: '',
+ roles: [],
+ redirect: '',
+ children: []
+ } as UserRouteItem
+}
+
+/**
+ * 按后端下发的菜单键,生成异步路由树。
+ *
+ * @param menuKeys `/auth/me` 返回的菜单键集合
+ * @returns 分组 → 页面的两级路由树(`component: 'Layout'` 由上层 formatAsyncRoutes 处理)
+ */
+export function buildXtRoutes(menuKeys: string[]): UserRouteItem[] {
+ const keys = new Set(menuKeys)
+ const visible = XT_MENUS.filter((m) => keys.has(m.key))
+ if (!visible.length) {
+ return []
+ }
+
+ const routes: UserRouteItem[] = []
+ for (const g of GROUPS) {
+ const items = visible.filter((m) => m.group === g.id)
+ if (!items.length) {
+ continue
+ }
+ routes.push({
+ id: g.id,
+ parentId: '0',
+ path: `/${g.id}`,
+ // 'Layout' 是约定的布局组件标识,formatAsyncRoutes 会替换成真实组件
+ component: 'Layout',
+ title: g.title,
+ icon: g.icon,
+ sort: g.sort,
+ type: 1,
+ status: '1',
+ hidden: false,
+ keepAlive: false,
+ affix: false,
+ breadcrumb: true,
+ showInTabs: false,
+ alwaysShow: true,
+ activeMenu: '',
+ permission: '',
+ roles: [],
+ redirect: items[0].path,
+ children: items
+ .sort((a, b) => a.sort - b.sort)
+ .map((m) => (m.ready ? readyItem(m) : placeholder(m)))
+ } as UserRouteItem)
+ }
+ return routes
+}
+
+/** 已实现页面 */
+function readyItem(menu: XtMenu): UserRouteItem {
+ return {
+ id: menu.key,
+ parentId: menu.group ?? '0',
+ path: menu.path,
+ component: menu.component!,
+ title: menu.title,
+ icon: menu.icon,
+ sort: menu.sort,
+ type: 2,
+ status: '1',
+ hidden: false,
+ keepAlive: false,
+ affix: false,
+ breadcrumb: true,
+ showInTabs: true,
+ alwaysShow: false,
+ activeMenu: '',
+ permission: '',
+ roles: [],
+ redirect: '',
+ children: []
+ } as UserRouteItem
+}
+
+/** 按菜单键取定义(给"待建"页显示说明用) */
+export function findMenu(key: string): XtMenu | undefined {
+ return XT_MENUS.find((m) => m.key === key)
+}
+
+/** 角色码 → 中文名(与后端 BuiltinRole 对齐) */
+export const ROLE_NAMES: Record = {
+ PLATFORM_ADMIN: '平台运营',
+ SCHOOL_ADMIN: '学校管理员',
+ HEAD_TEACHER: '班主任',
+ DEPUTY_TEACHER: '副班主任',
+ AUDITOR: '审核员',
+ STUDENT: '学生'
+}
diff --git a/frontend-pc/src/directives/hasPerm.ts b/frontend-pc/src/directives/hasPerm.ts
new file mode 100644
index 0000000..b615b59
--- /dev/null
+++ b/frontend-pc/src/directives/hasPerm.ts
@@ -0,0 +1,44 @@
+import type { Directive, DirectiveBinding } from 'vue'
+import { hasPermOr } from '@/utils/permission'
+
+/**
+ * 权限检查函数
+ * @description 检查元素是否具有指定的权限,如果没有权限则移除该元素
+ * @param el - 要检查的 DOM 元素
+ * @param binding - 指令绑定对象
+ * @throws {Error} 当权限参数格式不正确时抛出错误
+ */
+function checkPermission(el: HTMLElement, binding: DirectiveBinding) {
+ const { value } = binding
+
+ // 检查权限参数是否有效
+ if (!value || !Array.isArray(value) || value.length === 0) {
+ throw new Error('权限参数格式错误,示例:v-hasPerm="[\'home:btn:edit\',\'home:btn:delete\']"')
+ }
+
+ // 检查是否具有权限
+ const hasPermission = hasPermOr(value)
+ if (!hasPermission) {
+ el.parentNode?.removeChild(el)
+ }
+}
+
+/**
+ * 权限指令
+ * @description 用于控制元素的显示/隐藏,基于用户权限
+ * @example
+ * ```vue
+ *
+ *
+ * ```
+ */
+const hasPermDirective: Directive = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ checkPermission(el, binding)
+ },
+ updated(el: HTMLElement, binding: DirectiveBinding) {
+ checkPermission(el, binding)
+ }
+}
+
+export default hasPermDirective
diff --git a/frontend-pc/src/directives/hasRole.ts b/frontend-pc/src/directives/hasRole.ts
new file mode 100644
index 0000000..0499fd6
--- /dev/null
+++ b/frontend-pc/src/directives/hasRole.ts
@@ -0,0 +1,44 @@
+import type { Directive, DirectiveBinding } from 'vue'
+import { hasRoleOr } from '@/utils/permission'
+
+/**
+ * 角色检查函数
+ * @description 检查元素是否具有指定的角色,如果没有角色则移除该元素
+ * @param el - 要检查的 DOM 元素
+ * @param binding - 指令绑定对象
+ * @throws {Error} 当角色参数格式不正确时抛出错误
+ */
+function checkRole(el: HTMLElement, binding: DirectiveBinding) {
+ const { value } = binding
+
+ // 检查角色参数是否有效
+ if (!value || !Array.isArray(value) || value.length === 0) {
+ throw new Error('角色参数格式错误,示例:v-hasRole="[\'admin\',\'user\']"')
+ }
+
+ // 检查是否具有角色
+ const hasRole = hasRoleOr(value)
+ if (!hasRole) {
+ el.parentNode?.removeChild(el)
+ }
+}
+
+/**
+ * 角色权限指令
+ * @description 用于控制元素的显示/隐藏,基于用户角色
+ * @example
+ * ```vue
+ * 管理员可见
+ * 管理员或用户可见
+ * ```
+ */
+const hasRoleDirective: Directive = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ checkRole(el, binding)
+ },
+ updated(el: HTMLElement, binding: DirectiveBinding) {
+ checkRole(el, binding)
+ }
+}
+
+export default hasRoleDirective
diff --git a/frontend-pc/src/directives/index.ts b/frontend-pc/src/directives/index.ts
new file mode 100644
index 0000000..b50238e
--- /dev/null
+++ b/frontend-pc/src/directives/index.ts
@@ -0,0 +1,10 @@
+import type { App } from 'vue'
+import hasPerm from './hasPerm'
+import hasRole from './hasRole'
+
+export default {
+ install(Vue: App) {
+ Vue.directive('hasPerm', hasPerm)
+ Vue.directive('hasRole', hasRole)
+ }
+}
diff --git a/frontend-pc/src/hooks/app/index.ts b/frontend-pc/src/hooks/app/index.ts
new file mode 100644
index 0000000..42d70dd
--- /dev/null
+++ b/frontend-pc/src/hooks/app/index.ts
@@ -0,0 +1,2 @@
+export * from './useDept'
+export * from './useRole'
diff --git a/frontend-pc/src/hooks/app/useDept.ts b/frontend-pc/src/hooks/app/useDept.ts
new file mode 100644
index 0000000..94c7395
--- /dev/null
+++ b/frontend-pc/src/hooks/app/useDept.ts
@@ -0,0 +1,27 @@
+import type * as T from '@/apis/system/dept'
+import { ref } from 'vue'
+import { mapTree } from 'xe-utils'
+import { baseAPI } from '@/apis/system/dept'
+
+/** 部门模块 */
+export function useDept(options?: { onSuccess?: () => void }) {
+ const loading = ref(false)
+ const deptList = ref([])
+
+ const getDeptList = async () => {
+ try {
+ loading.value = true
+ const res = await baseAPI.getList()
+ deptList.value = mapTree(res.data, (i) => {
+ if (i.children?.length) {
+ i.children = i.children.filter((i) => i.status === '1')
+ }
+ return i
+ })
+ options?.onSuccess && options.onSuccess()
+ } finally {
+ loading.value = false
+ }
+ }
+ return { deptList, getDeptList, loading }
+}
diff --git a/frontend-pc/src/hooks/app/useRole.ts b/frontend-pc/src/hooks/app/useRole.ts
new file mode 100644
index 0000000..ce70ad1
--- /dev/null
+++ b/frontend-pc/src/hooks/app/useRole.ts
@@ -0,0 +1,21 @@
+import type * as T from '@/apis/system/role'
+import { ref } from 'vue'
+import { baseAPI } from '@/apis/system/role'
+
+/** 角色模块 */
+export function useRole() {
+ const loading = ref(false)
+ const roleList = ref([])
+ const total = ref(0)
+ const getRoleList = async () => {
+ try {
+ loading.value = true
+ const res = await baseAPI.getList({ page: 1, size: 99 })
+ roleList.value = res.data.records.filter((i) => i.status === '1')
+ total.value = res.data.total
+ } finally {
+ loading.value = false
+ }
+ }
+ return { roleList, getRoleList, loading, total }
+}
diff --git a/frontend-pc/src/hooks/index.ts b/frontend-pc/src/hooks/index.ts
new file mode 100644
index 0000000..b5656f1
--- /dev/null
+++ b/frontend-pc/src/hooks/index.ts
@@ -0,0 +1,11 @@
+export * from './useBreakpoint'
+export * from './useChart'
+export * from './useDevice'
+export * from './useDict'
+export * from './useLoading'
+export * from './usePagination'
+export * from './useRequest'
+export * from './useResetReactive'
+export * from './useRouteListener'
+export * from './useTable'
+export * from './useTheme'
diff --git a/frontend-pc/src/hooks/useBreakpoint.ts b/frontend-pc/src/hooks/useBreakpoint.ts
new file mode 100644
index 0000000..2c797ea
--- /dev/null
+++ b/frontend-pc/src/hooks/useBreakpoint.ts
@@ -0,0 +1,34 @@
+import type { ColProps } from '@arco-design/web-vue'
+import type { ComputedRef } from 'vue'
+import { useBreakpoints } from '@vueuse/core'
+import { computed } from 'vue'
+
+type ColBreakpoint = Pick
+type Breakpoint = keyof ColBreakpoint
+
+// 定义断点配置
+const BREAKPOINT_CONFIG = {
+ xs: 576, // <576px
+ sm: 576, // ≥576px
+ md: 768, // ≥768px
+ lg: 992, // ≥992px
+ xl: 1200, // ≥1200px
+ xxl: 1600 // ≥1600px
+} as const
+
+/**
+ * 响应式断点 Hook
+ * @returns {{ breakpoint: ComputedRef }} 当前激活的最大断点值
+ */
+export function useBreakpoint() {
+ const breakpoints = useBreakpoints(BREAKPOINT_CONFIG)
+
+ const currentBreakpoints = breakpoints.current() as ComputedRef
+
+ const breakpoint = computed(() => {
+ const arr = currentBreakpoints.value
+ return arr.length ? arr[arr.length - 1] : 'xs'
+ })
+
+ return { breakpoint }
+}
diff --git a/frontend-pc/src/hooks/useChart.ts b/frontend-pc/src/hooks/useChart.ts
new file mode 100644
index 0000000..2222113
--- /dev/null
+++ b/frontend-pc/src/hooks/useChart.ts
@@ -0,0 +1,30 @@
+import type { EChartsOption } from 'echarts'
+import { computed } from 'vue'
+import { useTheme } from '@/hooks'
+
+interface Options {
+ (isDark?: boolean): EChartsOption
+}
+
+/**
+ * 用于集中管理 ECharts 配置,提供完整类型提示
+ * @param sourceOption - ECharts 配置项
+ * @returns { option: ComputedRef, theme: ComputedRef } - 返回 ECharts 配置项和主题
+ */
+export function useChart(sourceOption: Options) {
+ const { isDark } = useTheme()
+
+ // echarts support https://echarts.apache.org/zh/theme-builder.html
+ const option = computed(() => {
+ return sourceOption(isDark.value)
+ })
+
+ const theme = computed(() => isDark.value ? 'dark' : undefined)
+
+ return {
+ /** ECharts 配置项 */
+ option,
+ /** ECharts 主题 */
+ theme
+ }
+}
diff --git a/frontend-pc/src/hooks/useDevice.ts b/frontend-pc/src/hooks/useDevice.ts
new file mode 100644
index 0000000..0a8305e
--- /dev/null
+++ b/frontend-pc/src/hooks/useDevice.ts
@@ -0,0 +1,17 @@
+import { useWindowSize } from '@vueuse/core'
+import { computed } from 'vue'
+
+/**
+ * 响应式布局容器固定宽度
+ *
+ * 大屏(>=1200px)
+ * 中屏(>=992px)
+ * 小屏(>=768px)
+ */
+export function useDevice() {
+ const { width } = useWindowSize()
+ const isDesktop = computed(() => width.value > 768)
+ const isMobile = computed(() => !isDesktop.value)
+
+ return { isMobile, isDesktop }
+}
diff --git a/frontend-pc/src/hooks/useDict.ts b/frontend-pc/src/hooks/useDict.ts
new file mode 100644
index 0000000..c2f0dae
--- /dev/null
+++ b/frontend-pc/src/hooks/useDict.ts
@@ -0,0 +1,37 @@
+import type { SelectOptionData } from '@arco-design/web-vue'
+import { computed } from 'vue'
+import { useDictStore } from '@/stores'
+
+/**
+ * 将字符串数组转换为对象类型
+ * @example
+ * ArrayToObject<['STATUS', 'GENDER']> // { STATUS: SelectOptionData[], GENDER: SelectOptionData[] }
+ */
+type ArrayToObject = {
+ [K in T[number]]: SelectOptionData[]
+}
+
+/**
+ * 字典数据 Hook
+ * @description 根据传入的字典代码数组,返回对应的字典数据对象
+ * @param codes - 字典代码数组,会自动推断字面量类型以提供类型提示
+ * @returns 返回包含字典数据的响应式对象,对象的键为 codes 中的元素
+ * @example
+ * const { dictData } = useDict(['STATUS', 'GENDER'] as const)
+ * // dictData.value.STATUS ✅ 有类型提示
+ * // dictData.value.GENDER ✅ 有类型提示
+ * // dictData.value.OTHER ❌ TypeScript 会报错
+ */
+export function useDict(codes: T) {
+ const dictStore = useDictStore()
+
+ const dictData = computed>(() => {
+ const result = {} as ArrayToObject
+ for (const code of codes) {
+ result[code as T[number]] = dictStore.dictData[code] || []
+ }
+ return result
+ })
+
+ return { dictData }
+}
diff --git a/frontend-pc/src/hooks/useLoading.ts b/frontend-pc/src/hooks/useLoading.ts
new file mode 100644
index 0000000..3e49152
--- /dev/null
+++ b/frontend-pc/src/hooks/useLoading.ts
@@ -0,0 +1,30 @@
+import { ref } from 'vue'
+
+/**
+ * 加载状态管理 Hook
+ * @description 提供加载状态管理的响应式 Hook,包含加载状态的设置、切换等功能
+ * @param initValue - 初始加载状态,默认为 false
+ * @returns 返回包含加载状态管理和操作方法的对象
+ * {{
+ * loading: Ref, // 加载状态的响应式引用
+ * setLoading: (value: boolean) => void, // 设置加载状态的函数
+ * toggle: () => void // 切换加载状态的函数
+ * }}
+ */
+export function useLoading(initValue = false) {
+ const loading = ref(initValue)
+
+ const setLoading = (value: boolean) => {
+ loading.value = value
+ }
+
+ const toggle = () => {
+ loading.value = !loading.value
+ }
+
+ return {
+ loading,
+ setLoading,
+ toggle
+ }
+}
diff --git a/frontend-pc/src/hooks/usePagination.ts b/frontend-pc/src/hooks/usePagination.ts
new file mode 100644
index 0000000..718506d
--- /dev/null
+++ b/frontend-pc/src/hooks/usePagination.ts
@@ -0,0 +1,90 @@
+import { reactive, toRefs, watch } from 'vue'
+import { useBreakpoint } from '@/hooks'
+
+type Callback = () => void
+
+type Options = {
+ defaultPageSize: number
+}
+
+/**
+ * 分页管理 Hook
+ * @description 提供分页状态管理的响应式 Hook,支持响应式断点,自动适配移动端显示
+ * @param callback - 分页变化时的回调函数
+ * @param options - 分页配置选项,默认每页显示 10 条
+ * @returns 包含分页状态和操作方法的响应式对象
+ * {{
+ * current: Ref, // 当前页码
+ * pageSize: Ref, // 每页显示数量
+ * total: Ref, // 总数据量
+ * pagination: Object, // 完整的分页配置对象
+ * changeCurrent: (page: number) => void, // 改变当前页码
+ * changePageSize: (size: number) => void, // 改变每页显示数量
+ * setTotal: (value: number) => void // 设置总数据量
+ * }}
+ * @example
+ * const { pagination, setTotal } = usePagination(() => {
+ * // 分页变化时的回调
+ * fetchData()
+ * })
+ */
+export function usePagination(callback: Callback, options: Options = { defaultPageSize: 10 }) {
+ // 获取响应式断点
+ const { breakpoint } = useBreakpoint()
+
+ // 创建响应式分页配置对象
+ const pagination = reactive({
+ showPageSize: true,
+ showTotal: true,
+ current: 1,
+ pageSize: options.defaultPageSize,
+ total: 0,
+ simple: false,
+ onChange: (size: number) => {
+ pagination.current = size
+ callback && callback()
+ },
+ onPageSizeChange: (size: number) => {
+ pagination.current = 1
+ pagination.pageSize = size
+ callback && callback()
+ }
+ })
+
+ // 监听断点变化,自动适配移动端显示
+ watch(
+ () => breakpoint.value,
+ () => {
+ // 在 xs(移动端)断点下使用简单模式
+ pagination.simple = ['xs'].includes(breakpoint.value)
+ // 在 xs(移动端)断点下不显示总数据量
+ pagination.showTotal = !['xs'].includes(breakpoint.value)
+ },
+ { immediate: true }
+ )
+
+ // 导出便捷方法
+ const changeCurrent = pagination.onChange
+ const changePageSize = pagination.onPageSizeChange
+
+ /**
+ * 设置总数据量
+ * @param value - 新的总数据量
+ */
+ function setTotal(value: number) {
+ pagination.total = value
+ }
+
+ // 导出响应式引用
+ const { current, pageSize, total } = toRefs(pagination)
+
+ return {
+ current,
+ pageSize,
+ total,
+ pagination,
+ changeCurrent,
+ changePageSize,
+ setTotal
+ }
+}
diff --git a/frontend-pc/src/hooks/useRequest.ts b/frontend-pc/src/hooks/useRequest.ts
new file mode 100644
index 0000000..96ad4c2
--- /dev/null
+++ b/frontend-pc/src/hooks/useRequest.ts
@@ -0,0 +1,90 @@
+import { ref } from 'vue'
+import { useLoading } from '@/hooks'
+
+/**
+ * 请求配置选项
+ */
+interface UseRequestOptions {
+ /** 默认值 */
+ defaultValue?: T
+ /** 是否立即执行请求,默认为 true */
+ immediate?: boolean
+ /** 初始加载状态,默认为 true */
+ isLoading?: boolean
+ /** 请求成功回调 */
+ onSuccess?: (data: T) => void
+ /** 请求失败回调 */
+ onError?: (error: any) => void
+}
+
+/**
+ * 请求管理 Hook
+ * @description 提供请求状态管理的响应式 Hook,支持自动/手动触发、错误处理、刷新等功能
+ * @template T - 响应数据类型
+ * @param api - API 请求函数,返回 Promise>
+ * @param options - 请求配置选项
+ * @returns 返回包含请求状态和操作方法的对象
+ * {{
+ * loading: Ref, // 加载状态的响应式引用
+ * data: Ref, // 响应数据的响应式引用
+ * error: Ref, // 错误信息的响应式引用
+ * run: () => Promise, // 手动触发请求的函数
+ * }}
+ * @example
+ * // 基础用法 - 立即执行
+ * const { loading, data } = useRequest(() => getUserInfo())
+ *
+ * // 手动触发
+ * const { loading, data, run } = useRequest(() => getUserInfo(), { immediate: false })
+ * run()
+ *
+ * // 带默认值和回调
+ * const { loading, data } = useRequest(
+ * () => getUserInfo(),
+ * {
+ * defaultValue: {},
+ * onSuccess: (data) => console.log('成功', data),
+ * onError: (error) => console.error('失败', error)
+ * }
+ * )
+ */
+export function useRequest(
+ api: () => Promise>,
+ options: UseRequestOptions = {}
+) {
+ const { defaultValue, immediate = true, isLoading = true, onSuccess, onError } = options
+ const { loading, setLoading } = useLoading(isLoading)
+ const data = ref(defaultValue as T)
+ const error = ref(null)
+
+ /**
+ * 执行请求
+ * @description 调用 API 并更新响应数据,处理加载状态和错误
+ */
+ async function run() {
+ try {
+ error.value = null
+ setLoading(true)
+ const res = await api()
+ data.value = res.data as T
+ onSuccess?.(res.data)
+ } catch (err) {
+ error.value = err
+ onError?.(err)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ // 立即执行请求
+ if (immediate) {
+ run()
+ }
+
+ return {
+ loading,
+ data,
+ error,
+ run
+ }
+}
diff --git a/frontend-pc/src/hooks/useResetReactive.ts b/frontend-pc/src/hooks/useResetReactive.ts
new file mode 100644
index 0000000..fec7ca2
--- /dev/null
+++ b/frontend-pc/src/hooks/useResetReactive.ts
@@ -0,0 +1,47 @@
+import { cloneDeep } from 'lodash-es'
+import { reactive } from 'vue'
+
+/**
+ * 响应式状态重置 Hook
+ * @description 创建一个可重置的响应式对象,支持将状态重置为初始值
+ * @template T - 状态对象的类型
+ * @param initialValue - 初始状态值
+ * @returns [state, reset] - 返回响应式状态和重置函数的元组
+ * @example
+ * const initialForm = {
+ * name: '',
+ * age: 0
+ * }
+ *
+ * const [form, resetForm] = useResetReactive(initialForm)
+ *
+ * // 修改表单
+ * form.name = 'John'
+ * form.age = 25
+ *
+ * // 重置表单到初始状态
+ * resetForm()
+ */
+export function useResetReactive(initialValue: T) {
+ /**
+ * 获取初始值的深拷贝
+ * @returns 初始值的深拷贝
+ */
+ const getInitialValue = () => cloneDeep(initialValue)
+
+ // 创建响应式状态
+ const state = reactive(getInitialValue())
+
+ /**
+ * 重置状态到初始值
+ * @description 先清空所有属性,然后用初始值重新赋值,确保引用不变
+ */
+ const reset = () => {
+ // 清空当前状态的所有属性
+ Object.keys(state).forEach((key) => delete (state as any)[key])
+ // 使用初始值重新赋值
+ Object.assign(state, getInitialValue())
+ }
+
+ return [state, reset] as const
+}
diff --git a/frontend-pc/src/hooks/useRouteListener.ts b/frontend-pc/src/hooks/useRouteListener.ts
new file mode 100644
index 0000000..55c5c97
--- /dev/null
+++ b/frontend-pc/src/hooks/useRouteListener.ts
@@ -0,0 +1,92 @@
+/**
+ * 路由变化监听 Hook
+ * @description 使用发布订阅模式管理路由变化,避免重复监听导致的性能问题
+ */
+
+import type { Handler } from 'mitt'
+import type { RouteLocationNormalized } from 'vue-router'
+import mitt from 'mitt'
+import { onUnmounted, ref } from 'vue'
+
+// 事件类型定义
+type RouteChangeEvent = {
+ to: RouteLocationNormalized
+ from?: RouteLocationNormalized
+}
+
+// 事件键定义
+const ROUTE_CHANGE_KEY = Symbol('ROUTE_CHANGE')
+
+// 事件发射器
+const emitter = mitt<{
+ [ROUTE_CHANGE_KEY]: RouteChangeEvent
+}>()
+
+// 最新路由状态
+const latestRoute = ref(null)
+
+/**
+ * 设置路由变化事件
+ * @param to 目标路由
+ * @param from 来源路由
+ */
+export function setRouteEmitter(to: RouteLocationNormalized, from?: RouteLocationNormalized) {
+ const event: RouteChangeEvent = { to, from }
+ emitter.emit(ROUTE_CHANGE_KEY, event)
+ latestRoute.value = to
+}
+
+/**
+ * 路由监听 Hook
+ * @returns 路由监听相关方法
+ */
+export function useRouteListener() {
+ // 监听器列表
+ const listeners = new Set>()
+
+ /**
+ * 监听路由变化
+ * @param handler 处理函数
+ * @param immediate 是否立即执行
+ */
+ function listenerRouteChange(
+ handler: (event: RouteChangeEvent) => void,
+ immediate = true
+ ) {
+ emitter.on(ROUTE_CHANGE_KEY, handler)
+ listeners.add(handler)
+
+ if (immediate && latestRoute.value) {
+ handler({ to: latestRoute.value })
+ }
+
+ return handler
+ }
+
+ /**
+ * 移除路由监听器
+ * @param handler 要移除的处理函数
+ */
+ function removeRouteListener(handler?: Handler) {
+ if (handler) {
+ emitter.off(ROUTE_CHANGE_KEY, handler)
+ listeners.delete(handler)
+ } else {
+ // 移除所有监听器
+ listeners.forEach((listener) => {
+ emitter.off(ROUTE_CHANGE_KEY, listener)
+ })
+ listeners.clear()
+ }
+ }
+
+ // 组件卸载时清理
+ onUnmounted(() => {
+ removeRouteListener()
+ })
+
+ return {
+ listenerRouteChange,
+ removeRouteListener
+ }
+}
diff --git a/frontend-pc/src/hooks/useTable.ts b/frontend-pc/src/hooks/useTable.ts
new file mode 100644
index 0000000..d489743
--- /dev/null
+++ b/frontend-pc/src/hooks/useTable.ts
@@ -0,0 +1,277 @@
+import type { TableData, TableInstance } from '@arco-design/web-vue'
+import { Message, Modal } from '@arco-design/web-vue'
+import { useBreakpoint, usePagination } from '@/hooks'
+
+interface Options {
+ formatResult?: (data: T[]) => U[]
+ onSuccess?: () => void
+ immediate?: boolean
+ rowKey?: keyof T
+ /** 是否开启跨页多选,开启后切换分页会保留已选中的 key */
+ crossPageSelect?: boolean
+ listAPI: (params: { page: number, size: number }) => Promise>> | Promise>
+ deleteAPI?: (ids: string[]) => Promise>
+}
+
+/** 删除操作的配置选项 */
+interface DeleteOptions {
+ /** 确认框标题 */
+ title?: string
+ /** 确认框内容 */
+ content?: string
+ /** 成功提示信息 */
+ successTip?: string
+ /** 是否显示确认框 */
+ showModal?: boolean
+}
+
+/**
+ * 表格数据管理 Hook
+ * @description 提供表格数据管理的响应式 Hook,支持分页、搜索、刷新、删除等功能
+ * @template T - 原始数据类型
+ * @template U - 格式化后的数据类型
+ * @param options - 表格配置选项
+ * @returns 表格相关的状态和方法
+ * @example
+ * const {
+ * loading,
+ * tableData,
+ * pagination,
+ * selectedKeys,
+ * search,
+ * refresh,
+ * handleDelete
+ * } = useTable({
+ * listAPI: (p) => getTableApi(p),
+ * formatResult: (data) => data.map(formatData),
+ * immediate: true,
+ * rowKey: 'id'
+ * })
+ */
+export function useTable(options: Options) {
+ const { formatResult, onSuccess, immediate = true, rowKey = 'id', crossPageSelect = false, listAPI, deleteAPI }
+ = options || {}
+
+ // 分页相关
+ const { pagination, setTotal } = usePagination(() => getTableData())
+ const loading = ref(false)
+ const tableData: Ref = ref([])
+
+ /**
+ * 获取表格数据
+ * @description 调用 API 获取数据,处理分页、格式化等逻辑
+ */
+ async function getTableData() {
+ try {
+ loading.value = true
+ const res = await listAPI({ page: pagination.current, size: pagination.pageSize })
+ // 处理返回的数据结构可能是分页或数组的情况
+ const data = !Array.isArray(res.data) ? res.data.records : res.data
+ tableData.value = formatResult ? formatResult(data) : data
+ // 设置总数据量
+ const total = !Array.isArray(res.data) ? res.data.total : data.length
+ setTotal(total)
+ onSuccess?.()
+ } finally {
+ loading.value = false
+ }
+ }
+
+ // 首次加载数据
+ immediate && getTableData()
+
+ // 多选
+ const selectedKeys = ref<(string | number)[]>([])
+
+ /** 当前页所有行的 key(用于跨页多选时合并) */
+ function getCurrentPageKeys() {
+ return (tableData.value as TableData[]).map((i) => i[rowKey as string])
+ }
+
+ /**
+ * 行选择回调
+ * @param rowKeys - 表格传入的选中行 key(可能仅为当前页,也可能含跨页;以当前页 key 为准做合并避免重复)
+ */
+ const select: TableInstance['onSelect'] = (rowKeys) => {
+ if (crossPageSelect) {
+ const pageKeys = getCurrentPageKeys()
+ const rest = selectedKeys.value.filter((k) => !pageKeys.includes(k))
+ // 只合并当前页的选中:避免表格传入的 rowKeys 含其它页时造成重复
+ const currentPageSelected = rowKeys.filter((k) => pageKeys.includes(k))
+ selectedKeys.value = [...new Set([...rest, ...currentPageSelected])]
+ } else {
+ selectedKeys.value = rowKeys
+ }
+ }
+
+ /**
+ * 全选回调
+ * @param checked - 是否全选
+ */
+ const selectAll: TableInstance['onSelectAll'] = (checked) => {
+ const arr = (tableData.value as TableData[]).filter((i) => !(i?.disabled ?? false))
+ const pageKeys = arr.map((i) => i[rowKey as string])
+ if (crossPageSelect) {
+ if (checked) {
+ const rest = selectedKeys.value.filter((k) => !pageKeys.includes(k))
+ selectedKeys.value = [...rest, ...pageKeys]
+ } else {
+ selectedKeys.value = selectedKeys.value.filter((k) => !pageKeys.includes(k))
+ }
+ } else {
+ selectedKeys.value = checked ? pageKeys : []
+ }
+ }
+
+ /** 获取选中的数据 */
+ const getSelectedData = () => {
+ return tableData.value.filter((i) => selectedKeys.value.includes(i[rowKey as string]))
+ }
+
+ /**
+ * 搜索
+ * @description 重置页码为1并重新获取数据
+ */
+ const search = () => {
+ selectedKeys.value = []
+ pagination.onChange(1)
+ }
+
+ /**
+ * 刷新表格数据
+ * @description 使用当前的分页参数重新获取数据
+ */
+ const refresh = () => {
+ getTableData()
+ }
+
+ /**
+ * 处理删除操作
+ * @description 支持删除确认框,自动处理删除后的刷新
+ * @param deleteApi - 删除操作的 API 函数
+ * @param options - 删除操作的配置选项
+ * @returns Promise 删除操作的结果
+ */
+ const handleDelete = async (
+ deleteApi: () => Promise>,
+ options?: DeleteOptions
+ ): Promise => {
+ const onDelete = async (isMultiple: boolean) => {
+ try {
+ const res = await deleteApi()
+ if (res.success) {
+ Message.success(options?.successTip || '删除成功!')
+ // 处理当前页码
+ const deleteNum = isMultiple ? selectedKeys.value.length : 1
+ const totalPage = Math.ceil((pagination.total - deleteNum) / pagination.pageSize)
+ if (pagination.current > totalPage) {
+ pagination.current = totalPage > 0 ? totalPage : 1
+ }
+ isMultiple && (selectedKeys.value = [])
+ getTableData()
+ }
+ return res.success
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log('删除失败', error)
+ return false
+ }
+ }
+
+ if (!(options?.showModal ?? true)) {
+ return onDelete(false)
+ }
+
+ Modal.warning({
+ title: options?.title || '提示',
+ content: options?.content || '是否确认删除?',
+ hideCancel: false,
+ maskClosable: false,
+ onBeforeOk: () => onDelete(true)
+ })
+ }
+
+ /**
+ * 删除单条数据
+ * @description 需在 options 中配置 deleteAPI
+ * @param row - 要删除的行数据
+ */
+ const onDelete = (row: U) => {
+ if (!deleteAPI) {
+ Message.error('deleteAPI 没有配置')
+ return
+ }
+ const ids = String(row[rowKey as keyof U])
+ return handleDelete(() => deleteAPI([ids]), { showModal: false })
+ }
+
+ /**
+ * 批量删除数据
+ * @description 需在 options 中配置 deleteAPI
+ */
+ const onBatchDelete = () => {
+ if (!deleteAPI) {
+ Message.error('deleteAPI 没有配置')
+ return
+ }
+ if (!selectedKeys.value.length) {
+ Message.warning('请选择要删除的数据')
+ return
+ }
+ handleDelete(() => deleteAPI(selectedKeys.value as string[]), {
+ title: '批量删除',
+ content: `确定要删除选中的 ${selectedKeys.value.length} 条数据吗?`
+ })
+ }
+
+ // 自定义扩展...
+ // 导入
+ const onImport = () => {
+ Message.warning('点击了导入')
+ }
+ // 导出
+ const onExport = () => {
+ Message.success('点击了导出')
+ }
+ // 下载模板
+ // const onDownloadTemplate = () => {}
+
+ // 响应式处理表格操作列固定状态
+ const { breakpoint } = useBreakpoint()
+ const fixed = computed(() => !['xs', 'sm'].includes(breakpoint.value) ? 'right' : undefined)
+
+ return {
+ /** 表格加载状态 */
+ loading,
+ /** 表格数据 */
+ tableData,
+ /** 获取表格数据 */
+ getTableData,
+ /** 搜索,页码会重置为1 */
+ search,
+ /** 分页的传参 */
+ pagination,
+ /** 选择的行keys */
+ selectedKeys,
+ /** 选择行 */
+ select,
+ /** 全选行 */
+ selectAll,
+ /** 获取选择的数据 */
+ getSelectedData,
+ /** 处理删除、批量删除 */
+ handleDelete,
+ /** 删除单条数据,需配置 deleteAPI */
+ onDelete,
+ /** 批量删除数据,需配置 deleteAPI */
+ onBatchDelete,
+ /** 刷新表格数据,页码会缓存 */
+ refresh,
+ /** 操作列在小屏场景下不固定在右侧 */
+ fixed,
+ /** 导入 */
+ onImport,
+ /** 导出 */
+ onExport
+ }
+}
diff --git a/frontend-pc/src/hooks/useTheme.ts b/frontend-pc/src/hooks/useTheme.ts
new file mode 100644
index 0000000..e198327
--- /dev/null
+++ b/frontend-pc/src/hooks/useTheme.ts
@@ -0,0 +1,75 @@
+import { generate, getRgbStr } from '@arco-design/color'
+import { useDark, useToggle } from '@vueuse/core'
+import { computed } from 'vue'
+import { useAppStore } from '@/stores'
+
+/**
+ * 主题管理 Hook
+ * @description 使用 vueuse 的 useDark 来管理主题状态
+ */
+export function useTheme() {
+ const appStore = useAppStore()
+
+ /** 是否为暗色主题 */
+ const isDark = useDark({
+ // 主题切换目标元素
+ selector: 'body',
+ // 主题属性名
+ attribute: 'arco-theme',
+ // 暗色主题值
+ valueDark: 'dark',
+ // 亮色主题值
+ valueLight: 'light',
+ // 本地存储键名
+ storageKey: 'arco-theme',
+ // 初始值
+ initialValue: 'light'
+ })
+
+ /** 当前主题 */
+ const theme = computed(() => isDark.value ? 'dark' : 'light')
+
+ /** 主题切换函数定义 */
+ const onToggle = useToggle(isDark)
+
+ /** 主题切换函数 */
+ function toggleTheme() {
+ onToggle()
+ setThemeColor(appStore.themeColor)
+ }
+
+ /**
+ * 设置主题色
+ * 生成主题色的色阶并应用到 CSS 变量
+ * @param color - 主题色值
+ */
+ function setThemeColor(color: string) {
+ if (!color) return
+ appStore.themeColor = color
+ const themeColors = generate(color, {
+ list: true,
+ dark: isDark.value
+ }) as string[]
+ themeColors.forEach((color, index) => {
+ document.body.style.setProperty(`--primary-${index + 1}`, getRgbStr(color))
+ })
+ }
+
+ /**
+ * 初始化主题设置
+ * 在应用启动时设置初始主题色
+ */
+ function initThemeColor() {
+ if (appStore.themeColor) {
+ setThemeColor(appStore.themeColor)
+ }
+ }
+
+ return {
+ isDark,
+ theme,
+ toggleTheme,
+ setThemeColor,
+ initThemeColor
+ }
+}
diff --git a/frontend-pc/src/icons/custom-icons.json b/frontend-pc/src/icons/custom-icons.json
new file mode 100644
index 0000000..569b97d
--- /dev/null
+++ b/frontend-pc/src/icons/custom-icons.json
@@ -0,0 +1,197 @@
+{
+ "prefix": "custom",
+ "icons": {
+ "borders": {
+ "body": "",
+ "width": 48,
+ "height": 48
+ },
+ "table-size": {
+ "body": "",
+ "width": 48,
+ "height": 48
+ },
+ "tree-add": {
+ "body": "",
+ "width": 48,
+ "height": 48
+ },
+ "tree-reduce": {
+ "body": "",
+ "width": 48,
+ "height": 48
+ },
+ "alipay": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "avatar-man": {
+ "body": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "avatar-woman": {
+ "body": "\r\n \r\n \r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "backtop": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-all": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-close": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-css": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-dir": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-excel": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-exe": {
+ "body": "\r\n \r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-html": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-image": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-js": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-json": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-music": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-open": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-other": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-pdf": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-ppt": {
+ "body": "\r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-rar": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-txt": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-video": {
+ "body": "\r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-wps": {
+ "body": "\r\n \r\n \r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "file-zip": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "file": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "icon-msg": {
+ "body": "",
+ "width": 20,
+ "height": 20
+ },
+ "icon-notice": {
+ "body": "",
+ "width": 20,
+ "height": 20
+ },
+ "icon-num": {
+ "body": "",
+ "width": 20,
+ "height": 20
+ },
+ "icon-user": {
+ "body": "",
+ "width": 1024,
+ "height": 1024
+ },
+ "icon-wait": {
+ "body": "",
+ "width": 20,
+ "height": 20
+ },
+ "upload-file": {
+ "body": "\r\n ",
+ "width": 1228,
+ "height": 1024
+ },
+ "upload-folder": {
+ "body": "\r\n \r\n ",
+ "width": 1024,
+ "height": 1024
+ },
+ "vue": {
+ "body": "\r\n ",
+ "width": 128,
+ "height": 128
+ },
+ "wechat": {
+ "body": "\r\n ",
+ "width": 1024,
+ "height": 1024
+ }
+ },
+ "width": 48,
+ "height": 48
+}
diff --git a/frontend-pc/src/icons/index.ts b/frontend-pc/src/icons/index.ts
new file mode 100644
index 0000000..94b04a9
--- /dev/null
+++ b/frontend-pc/src/icons/index.ts
@@ -0,0 +1,6 @@
+import IconParkOutlineIcons from '@iconify-json/icon-park-outline/icons.json'
+import { addCollection } from '@iconify/vue'
+import CustomIcons from '@/icons/custom-icons.json'
+
+addCollection(IconParkOutlineIcons)
+addCollection(CustomIcons)
diff --git a/frontend-pc/src/layout/LayoutColumns.vue b/frontend-pc/src/layout/LayoutColumns.vue
new file mode 100644
index 0000000..fbec7c7
--- /dev/null
+++ b/frontend-pc/src/layout/LayoutColumns.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/LayoutDefault.vue b/frontend-pc/src/layout/LayoutDefault.vue
new file mode 100644
index 0000000..dc1c439
--- /dev/null
+++ b/frontend-pc/src/layout/LayoutDefault.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/LayoutMix.vue b/frontend-pc/src/layout/LayoutMix.vue
new file mode 100644
index 0000000..725bccd
--- /dev/null
+++ b/frontend-pc/src/layout/LayoutMix.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/LayoutTop.vue b/frontend-pc/src/layout/LayoutTop.vue
new file mode 100644
index 0000000..9fcd390
--- /dev/null
+++ b/frontend-pc/src/layout/LayoutTop.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Asider/index.vue b/frontend-pc/src/layout/components/Asider/index.vue
new file mode 100644
index 0000000..eaa748e
--- /dev/null
+++ b/frontend-pc/src/layout/components/Asider/index.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Breadcrumb/index.vue b/frontend-pc/src/layout/components/Breadcrumb/index.vue
new file mode 100644
index 0000000..9b203b7
--- /dev/null
+++ b/frontend-pc/src/layout/components/Breadcrumb/index.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+ {{ lastTitle || item.meta.title }}
+
+
+
+ {{ item.meta.title }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Header/index.vue b/frontend-pc/src/layout/components/Header/index.vue
new file mode 100644
index 0000000..b7d31d7
--- /dev/null
+++ b/frontend-pc/src/layout/components/Header/index.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/HeaderRightBar/Notice.vue b/frontend-pc/src/layout/components/HeaderRightBar/Notice.vue
new file mode 100644
index 0000000..7112e24
--- /dev/null
+++ b/frontend-pc/src/layout/components/HeaderRightBar/Notice.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+ {{ tab.title }}({{ tab.count }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue b/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue
new file mode 100644
index 0000000..d7f8d0c
--- /dev/null
+++ b/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+ 界面显示
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue b/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue
new file mode 100644
index 0000000..be6d771
--- /dev/null
+++ b/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ props.name }}
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/HeaderRightBar/index.vue b/frontend-pc/src/layout/components/HeaderRightBar/index.vue
new file mode 100644
index 0000000..1ab5d43
--- /dev/null
+++ b/frontend-pc/src/layout/components/HeaderRightBar/index.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Logo.vue b/frontend-pc/src/layout/components/Logo.vue
new file mode 100644
index 0000000..d6afe62
--- /dev/null
+++ b/frontend-pc/src/layout/components/Logo.vue
@@ -0,0 +1,85 @@
+
+
+
+
+ 填报通
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Main.vue b/frontend-pc/src/layout/components/Main.vue
new file mode 100644
index 0000000..dced06a
--- /dev/null
+++ b/frontend-pc/src/layout/components/Main.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Menu/MenuIcon.vue b/frontend-pc/src/layout/components/Menu/MenuIcon.vue
new file mode 100644
index 0000000..f243a09
--- /dev/null
+++ b/frontend-pc/src/layout/components/Menu/MenuIcon.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Menu/MenuItem.vue b/frontend-pc/src/layout/components/Menu/MenuItem.vue
new file mode 100644
index 0000000..7eb491d
--- /dev/null
+++ b/frontend-pc/src/layout/components/Menu/MenuItem.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+ {{ item?.meta?.title }}
+
+
+ {{ item?.meta?.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/MenuFoldBtn.vue b/frontend-pc/src/layout/components/MenuFoldBtn.vue
new file mode 100644
index 0000000..ca8faad
--- /dev/null
+++ b/frontend-pc/src/layout/components/MenuFoldBtn.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/OneLevelMenu/index.vue b/frontend-pc/src/layout/components/OneLevelMenu/index.vue
new file mode 100644
index 0000000..f6fe9b3
--- /dev/null
+++ b/frontend-pc/src/layout/components/OneLevelMenu/index.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Tabs/MagicIcon.vue b/frontend-pc/src/layout/components/Tabs/MagicIcon.vue
new file mode 100644
index 0000000..050bb5e
--- /dev/null
+++ b/frontend-pc/src/layout/components/Tabs/MagicIcon.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue b/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue
new file mode 100644
index 0000000..23b237f
--- /dev/null
+++ b/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/components/Tabs/index.vue b/frontend-pc/src/layout/components/Tabs/index.vue
new file mode 100644
index 0000000..88e8194
--- /dev/null
+++ b/frontend-pc/src/layout/components/Tabs/index.vue
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+ {{ item.meta?.title }}
+
+
+
+
+ 关闭左侧
+
+
+
+ 关闭右侧
+
+
+
+ 关闭其他
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 关闭左侧
+
+
+
+ 关闭右侧
+
+
+
+ 关闭其他
+
+
+
+ 关闭全部
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/layout/hooks/useLevelMenu.ts b/frontend-pc/src/layout/hooks/useLevelMenu.ts
new file mode 100644
index 0000000..b604a66
--- /dev/null
+++ b/frontend-pc/src/layout/hooks/useLevelMenu.ts
@@ -0,0 +1,123 @@
+import type { CSSProperties } from 'vue'
+import type { RouteLocationNormalizedGeneric, RouteRecordRaw } from 'vue-router'
+import { eachTree, searchTree } from 'xe-utils'
+import { useRouteListener } from '@/hooks'
+import { useAppStore, useRouteStore } from '@/stores'
+import { filterTree } from '@/utils'
+import { isExternal } from '@/utils/validate'
+
+type Options = {
+ oneActivePathChange?: (path: string) => void
+}
+
+/** 获取一级菜单,二级菜单的hooks */
+export function useLevelMenu(options?: Options) {
+ const route = useRoute()
+ const router = useRouter()
+ const appStore = useAppStore()
+ const routeStore = useRouteStore()
+ const { listenerRouteChange } = useRouteListener()
+
+ const menuTheme = computed(() => appStore.isMenuDark ? 'dark' : 'light')
+
+ // 获取菜单样式
+ const getMenuStyle = computed(() => {
+ return { backgroundColor: menuTheme.value === 'dark' ? 'var(--color-menu-dark-bg)' : 'var(--color-menu-light-bg)' } as CSSProperties
+ })
+
+ // 克隆一份路由,避免直接操作原始路由
+ const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[]
+ const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[]
+
+ // 一级菜单
+ const oneLevelMenus = ref([])
+
+ // 二级菜单
+ const twoLevelMenus = computed(() => {
+ const path = route.matched[0].path
+ return showMenuList.find((i) => i.path === path)?.children || []
+ })
+
+ // 一级菜单选中的路由
+ const oneActiveRoute = ref(null)
+ const oneActivePath = ref('')
+ const getOneActiveRoute = (to: RouteLocationNormalizedGeneric) => {
+ let path = to.path
+ if (to?.meta?.activeMenu) {
+ path = to.meta.activeMenu
+ }
+ const arr = searchTree(showMenuList, (i) => i.path === path)
+ oneActivePath.value = arr?.[0]?.path
+ return arr?.[0]
+ }
+
+ // 二级菜单选中的路由
+ const twoActivePath = ref('')
+ const getTwoActivePath = (to: RouteLocationNormalizedGeneric) => {
+ let path = to.path
+ if (to?.meta?.activeMenu) {
+ path = to.meta.activeMenu
+ }
+ return path
+ }
+
+ listenerRouteChange(({ to }) => {
+ oneActiveRoute.value = getOneActiveRoute(to)
+ twoActivePath.value = getTwoActivePath(to)
+ })
+
+ // 获取一级菜单
+ function getOneLevelMenus() {
+ const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[]
+ const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[]
+ eachTree(showMenuList, (i) => {
+ if (i?.children?.length === 1 && i?.meta?.alwaysShow !== true) {
+ if (i.meta) {
+ i.meta.title = i.meta?.title || i.children?.[0]?.meta?.title
+ i.meta.icon = i.meta?.icon || i.children?.[0]?.meta?.icon
+ }
+ delete i.children
+ }
+ })
+ oneLevelMenus.value = showMenuList
+ }
+
+ // 一级菜单点击事件
+ function handleOneMenuItemClick(path: string) {
+ if (isExternal(path)) {
+ window.open(path)
+ return
+ }
+ options?.oneActivePathChange?.(path)
+ oneActivePath.value = path
+ const obj = oneLevelMenus.value.find((i) => i.path === path)
+ if (obj?.redirect === 'noRedirect' || !obj?.redirect) {
+ router.push({ path: obj?.children?.[0]?.path })
+ return
+ }
+ router.push({ path: (obj?.redirect as string) || path })
+ }
+
+ // 二级菜单点击事件
+ function handleTwoMenuItemClick(path: string) {
+ if (isExternal(path)) {
+ window.open(path)
+ return
+ }
+ twoActivePath.value = path
+ router.push({ path })
+ }
+
+ return {
+ menuTheme,
+ getMenuStyle,
+ oneLevelMenus,
+ twoLevelMenus,
+ oneActiveRoute,
+ oneActivePath,
+ twoActivePath,
+ getOneLevelMenus,
+ handleOneMenuItemClick,
+ handleTwoMenuItemClick
+ }
+}
diff --git a/frontend-pc/src/layout/hooks/useMenu.ts b/frontend-pc/src/layout/hooks/useMenu.ts
new file mode 100644
index 0000000..1d9bdc1
--- /dev/null
+++ b/frontend-pc/src/layout/hooks/useMenu.ts
@@ -0,0 +1,101 @@
+import type { CSSProperties } from 'vue'
+import type { RouteRecordRaw } from 'vue-router'
+import { computed } from 'vue'
+import { eachTree } from 'xe-utils'
+import { useDevice, useRouteListener } from '@/hooks'
+import { useAppStore, useRouteStore } from '@/stores'
+import { filterTree } from '@/utils'
+import { isExternal } from '@/utils/validate'
+
+/**
+ * 菜单管理 Hooks
+ * 用于获取和处理应用程序的菜单列表
+ * 提供菜单的过滤、展平处理等功能
+ * @returns {object} 包含处理后菜单列表的响应式对象
+ */
+export function useMenu() {
+ const router = useRouter()
+ // 路由存储实例,用于获取原始路由配置
+ const routeStore = useRouteStore()
+ const appStore = useAppStore()
+ const { listenerRouteChange } = useRouteListener()
+ const { isDesktop } = useDevice()
+
+ // 是否折叠菜单
+ const collapsed = computed(() =>
+ !isDesktop.value ? false : appStore.isMenuCollapsed
+ )
+
+ // 菜单触发器配置
+ const menuTriggerProps = {
+ animationName: 'slide-dynamic-origin'
+ }
+
+ // 菜单主题
+ const menuTheme = computed(() => appStore.isMenuDark ? 'dark' : 'light')
+
+ // 获取菜单样式
+ const getMenuStyle = computed(() => {
+ return { backgroundColor: menuTheme.value === 'dark' ? 'var(--color-menu-dark-bg)' : 'var(--color-menu-light-bg)' } as CSSProperties
+ })
+
+ /**
+ * 处理后的菜单列表
+ * 响应式计算属性,当路由配置变化时自动更新,
+ * > hidden:false那么代表这个路由项显示在左侧菜单栏中
+ * > 子项chidren只有一个hidden:false的子元素时, 且alwaysShow为false, 那么这个子项会被展平到父项中
+ * 包含以下处理逻辑:
+ * 1. 深拷贝原始路由配置避免直接修改
+ * 2. 过滤掉设置为隐藏的菜单项
+ * 3. 展平只有一个子项的菜单项(提升用户体验)
+ */
+ const menuList = computed(() => {
+ // 深拷贝路由配置,防止修改原始数据
+ const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[]
+
+ // 过滤出非隐藏的菜单(meta.hidden !== false)
+ const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[]
+
+ // 遍历处理菜单树,展平只有一个子项的菜单项
+ eachTree(showMenuList, (i) => {
+ if (i?.children?.length === 1 && i?.meta?.alwaysShow !== true) {
+ if (i.meta) {
+ i.meta.title = i.meta?.title || i.children?.[0]?.meta?.title
+ i.meta.icon = i.meta?.icon || i.children?.[0]?.meta?.icon
+ }
+ i.path = i.children?.[0]?.path
+ delete i.children
+ }
+ })
+ return showMenuList
+ })
+
+ const selectedKeys = ref([])
+
+ function handleMenuItemClick(key: string) {
+ if (isExternal(key)) {
+ window.open(key)
+ return
+ }
+ selectedKeys.value = [key]
+ router.push({ path: key })
+ }
+
+ listenerRouteChange(({ to }) => {
+ if (to?.meta?.activeMenu) {
+ selectedKeys.value = [to.meta.activeMenu]
+ return
+ }
+ selectedKeys.value = [to.path]
+ })
+
+ return {
+ menuTheme,
+ getMenuStyle,
+ menuList,
+ menuTriggerProps,
+ selectedKeys,
+ collapsed,
+ handleMenuItemClick
+ }
+}
diff --git a/frontend-pc/src/layout/index.vue b/frontend-pc/src/layout/index.vue
new file mode 100644
index 0000000..24525fb
--- /dev/null
+++ b/frontend-pc/src/layout/index.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-pc/src/main.ts b/frontend-pc/src/main.ts
new file mode 100644
index 0000000..a4af86e
--- /dev/null
+++ b/frontend-pc/src/main.ts
@@ -0,0 +1,39 @@
+import { Card, Drawer, Modal } from '@arco-design/web-vue'
+// 额外引入 Arco Design Icon图标库
+import ArcoVueIcon from '@arco-design/web-vue/es/icon'
+// ⚠️ Arco 组件样式必须显式引入:
+// vite.config.ts 里 ArcoResolver 配了 sideEffect:false(样式不随组件自动引入),
+// 原本靠 @arco-themes/vue-gi-demo 主题包带入;我们换成自己的主题后就没人带了。
+// 不引的后果是「组件能用但完全没样式」,页面直接散架,且构建不报错。
+import '@arco-design/web-vue/dist/arco.css'
+import { createApp } from 'vue'
+// 状态管理
+import pinia from '@/stores'
+import App from './App.vue'
+// 自定义指令
+import directives from './directives'
+import router from './router'
+// 导入本项目主题(内部会覆盖 Arco 默认变量)
+import '@/styles/arco-ui/index.less'
+// 使用动画库
+import 'animate.css/animate.min.css'
+// 自定义过渡动画
+import '@/styles/css/transition.css'
+// 导入全局scss主文件
+import '@/styles/index.scss'
+// 注册离线图标集
+import '@/icons/index'
+
+// 对特定组件进行默认配置
+Card.props.bordered = false
+
+const app = createApp(App)
+Modal._context = app._context
+Drawer._context = app._context
+
+app.use(router)
+app.use(pinia)
+app.use(ArcoVueIcon)
+app.use(directives)
+
+app.mount('#app')
diff --git a/frontend-pc/src/router/guard/index.ts b/frontend-pc/src/router/guard/index.ts
new file mode 100644
index 0000000..903d133
--- /dev/null
+++ b/frontend-pc/src/router/guard/index.ts
@@ -0,0 +1,31 @@
+/** @file 路由守卫配置模块 - 处理路由导航守卫和权限控制 */
+
+import type { Router } from 'vue-router'
+import { setRouteEmitter } from '@/hooks'
+import { setupPermissionGuard } from './setupPermissionGuard'
+
+/**
+ * 设置页面路由守卫
+ * @description 处理路由变化时的页面级操作,如路由变化事件通知
+ * @param router - Vue Router 实例
+ */
+export const setupPageGuard = (router: Router) => {
+ router.beforeEach((to, from) => {
+ // 触发路由变化事件,通知所有监听器
+ setRouteEmitter(to, from)
+ })
+}
+
+/**
+ * 创建路由守卫
+ * @description 统一配置所有路由守卫,包括:
+ * 1. 页面路由守卫 - 处理路由变化事件
+ * 2. 权限路由守卫 - 处理路由访问权限
+ * @param router - Vue Router 实例
+ */
+export default function createRouteGuard(router: Router) {
+ // 设置页面路由守卫
+ setupPageGuard(router)
+ // 设置权限路由守卫
+ setupPermissionGuard(router)
+}
diff --git a/frontend-pc/src/router/guard/setupPermissionGuard.ts b/frontend-pc/src/router/guard/setupPermissionGuard.ts
new file mode 100644
index 0000000..3cafe56
--- /dev/null
+++ b/frontend-pc/src/router/guard/setupPermissionGuard.ts
@@ -0,0 +1,89 @@
+/** @file 路由权限守卫模块 - 处理路由访问权限和动态路由加载 */
+
+import type { RouteLocationNormalized, Router } from 'vue-router'
+import { Message } from '@arco-design/web-vue'
+import { useUserStore } from '@/stores'
+import { getToken } from '@/utils/auth'
+
+/** 免登录白名单路径 */
+const WHITE_LIST = ['/login', '/404']
+
+/** 是否已生成动态路由 */
+let isDynamicRoutesGenerated = false
+
+/**
+ * 处理动态路由加载
+ * @param router - 路由实例
+ * @param to - 目标路由
+ */
+async function handleDynamicRoutes(
+ router: Router,
+ to: RouteLocationNormalized
+): Promise<{ path: string, query?: any, replace: boolean }> {
+ const userStore = useUserStore()
+
+ try {
+ await userStore.generateRoutes()
+ // 标记动态路由已生成
+ isDynamicRoutesGenerated = true
+ // 确保路由添加完成后返回目标路由
+ return { ...to, replace: true }
+ } catch (error) {
+ // 发生错误时重置用户状态并跳转登录页
+ userStore.resetToken()
+ const errorMessage = error instanceof Error ? error.message : '路由守卫过程发生错误'
+ Message.error(errorMessage)
+ return { path: `/login`, query: { redirect: to.path }, replace: true }
+ }
+}
+
+/**
+ * 初始化路由守卫
+ * @param router - 路由实例
+ */
+export const setupPermissionGuard = (router: Router): void => {
+ router.beforeEach(async (to, _from, next) => {
+ const token = getToken()
+ const userStore = useUserStore()
+
+ // 已登录状态
+ if (token) {
+ if (to.path === '/login') {
+ // 已登录时访问登录页,直接放行(通常会被重定向到首页)
+ next()
+ return
+ }
+
+ // 只在首次访问或路由未生成时处理动态路由
+ if (!isDynamicRoutesGenerated) {
+ try {
+ const redirectRoute = await handleDynamicRoutes(router, to)
+ next(redirectRoute)
+ } catch {
+ // 如果出错,清空角色信息并重定向到登录页
+ userStore.resetToken()
+ // 重置路由生成标志
+ isDynamicRoutesGenerated = false
+ next(`/login?redirect=${to.path}`)
+ }
+ return
+ }
+
+ // 正常访问
+ next()
+ return
+ }
+
+ // 未登录状态时重置路由生成标志
+ isDynamicRoutesGenerated = false
+
+ if (WHITE_LIST.includes(to.path)) {
+ // 白名单路径直接访问
+ next()
+ return
+ }
+
+ // 其他路径重定向到登录页
+ next('/login')
+ })
+}
diff --git a/frontend-pc/src/router/index.ts b/frontend-pc/src/router/index.ts
new file mode 100644
index 0000000..4a864b9
--- /dev/null
+++ b/frontend-pc/src/router/index.ts
@@ -0,0 +1,99 @@
+/** @file 路由配置模块 - 处理路由的创建、守卫和重置 */
+
+import type { RouteRecordRaw } from 'vue-router'
+import { createRouter, createWebHashHistory } from 'vue-router'
+import createRouteGuard from '@/router/guard'
+import { useRouteStore } from '@/stores'
+
+/** 默认布局组件 */
+const Layout = () => import('@/layout/index.vue')
+
+/** 基础路由配置 */
+const baseRoutes: RouteRecordRaw[] = [
+ {
+ path: '/redirect',
+ component: Layout,
+ meta: { hidden: true },
+ children: [
+ {
+ path: '/redirect/:path(.*)',
+ component: () => import('@/views/redirect/index.vue')
+ }
+ ]
+ },
+ {
+ path: '/login',
+ component: () => import('@/views/login/index.vue'),
+ meta: { hidden: true }
+ }
+]
+
+/** 错误页面路由配置 */
+const errorRoutes: RouteRecordRaw[] = [
+ {
+ path: '/:pathMatch(.*)*',
+ component: () => import('@/views/error/404.vue'),
+ meta: { hidden: true }
+ },
+ {
+ path: '/403',
+ component: () => import('@/views/error/403.vue'),
+ meta: { hidden: true }
+ }
+]
+
+/** 主页路由配置 */
+const homeRoute: RouteRecordRaw = {
+ path: '/',
+ name: 'Home',
+ component: Layout,
+ redirect: '/dashboard',
+ meta: { hidden: false },
+ children: [
+ {
+ path: '/dashboard',
+ component: () => import('@/views/dashboard/index.vue'),
+ name: 'Dashboard',
+ meta: {
+ title: '工作台',
+ icon: 'icon-park-outline:workbench',
+ affix: true,
+ hidden: false,
+ breadcrumb: false
+ }
+ }
+ ]
+}
+
+/** 静态路由配置 */
+export const constantRoutes: RouteRecordRaw[] = [...baseRoutes, ...errorRoutes, homeRoute]
+
+/** 创建路由实例 */
+const router = createRouter({
+ history: createWebHashHistory(import.meta.env.BASE_URL),
+ routes: constantRoutes,
+ scrollBehavior: () => ({ left: 0, top: 0 })
+})
+
+// 创建路由守卫
+createRouteGuard(router)
+
+/** 重置路由配置 - 清除所有动态添加的路由 */
+export function resetRouter(): void {
+ try {
+ const routeStore = useRouteStore()
+ // 移除所有动态路由
+ routeStore.asyncRoutes.forEach((route) => {
+ const { name } = route
+ if (name && router.hasRoute(name)) {
+ router.removeRoute(name)
+ }
+ })
+ } catch (error) {
+ console.error('重置路由失败:', error)
+ // 降级处理:刷新页面以重置路由状态
+ window.location.reload()
+ }
+}
+
+export default router
diff --git a/frontend-pc/src/stores/index.ts b/frontend-pc/src/stores/index.ts
new file mode 100644
index 0000000..04e3879
--- /dev/null
+++ b/frontend-pc/src/stores/index.ts
@@ -0,0 +1,14 @@
+import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
+export * from './useAppStore'
+export * from './useDictStore'
+export * from './useMenuBadgeStore'
+export * from './useRouteStore'
+export * from './useTabsStore'
+export * from './useUserStore'
+
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
+
+export default pinia
diff --git a/frontend-pc/src/stores/useAppStore.ts b/frontend-pc/src/stores/useAppStore.ts
new file mode 100644
index 0000000..3fb6e8f
--- /dev/null
+++ b/frontend-pc/src/stores/useAppStore.ts
@@ -0,0 +1,42 @@
+import { defineStore } from 'pinia'
+import { computed, reactive, toRefs } from 'vue'
+import defaultSettings from '@/config/setting.json'
+
+/**
+ * App Store 的核心设置逻辑
+ */
+const storeSetup = () => {
+ // 初始化 App 配置
+ const settingConfig = reactive({ ...defaultSettings }) as App.SettingConfig
+
+ /**
+ * 计算页面切换动画类名
+ * 根据配置决定是否启用动画以及使用哪种动画模式
+ */
+ const pageTransitionName = computed(() =>
+ settingConfig.isTransitionEnabled ? settingConfig.transitionName : ''
+ )
+
+ /**
+ * 设置菜单折叠状态
+ * @param collapsed - 是否折叠
+ */
+ const setMenuCollapse = (collapsed: boolean) => {
+ settingConfig.isMenuCollapsed = collapsed
+ }
+
+ /** 恢复为 setting.json 中的默认配置 */
+ const resetSettings = () => {
+ Object.assign(settingConfig, defaultSettings)
+ }
+
+ return {
+ ...toRefs(settingConfig),
+ pageTransitionName,
+ setMenuCollapse,
+ resetSettings
+ }
+}
+
+// 创建并导出 App Store,启用持久化存储
+export const useAppStore = defineStore('app', storeSetup, { persist: true })
diff --git a/frontend-pc/src/stores/useDictStore.ts b/frontend-pc/src/stores/useDictStore.ts
new file mode 100644
index 0000000..da72542
--- /dev/null
+++ b/frontend-pc/src/stores/useDictStore.ts
@@ -0,0 +1,66 @@
+import type { SelectOptionData } from '@arco-design/web-vue'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { getDictData as getDictDataApi } from '@/apis/system/dict'
+
+/**
+ * 字典数据类型定义
+ */
+type DictDataMap = Record
+
+/**
+ * 字典 Store 的核心设置逻辑
+ * 用于管理系统字典数据的状态
+ */
+const storeSetup = () => {
+ /**
+ * 存储字典数据的响应式对象
+ * key: 字典类型
+ * value: 字典选项数组
+ */
+ const dictData = ref({})
+
+ /**
+ * 获取字典数据
+ * 从后端 API 获取字典数据并更新到 store 中
+ * @throws 当 API 请求失败时抛出错误
+ */
+ const getDictData = async () => {
+ try {
+ const res = await getDictDataApi()
+ if (res?.data) {
+ dictData.value = res.data
+ }
+ } catch (error) {
+ console.error('获取字典数据失败:', error)
+ throw error
+ }
+ }
+
+ /**
+ * 获取指定类型的字典数据
+ * @param dictType - 字典类型
+ * @returns 对应类型的字典选项数组,如果不存在则返回空数组
+ */
+ const getDictOptions = (dictType: string): SelectOptionData[] => {
+ return dictData.value[dictType] || []
+ }
+
+ /**
+ * 重置字典数据
+ * 清空所有已加载的字典数据
+ */
+ const resetDict = () => {
+ dictData.value = {}
+ }
+
+ return {
+ dictData,
+ getDictData,
+ getDictOptions,
+ resetDict
+ }
+}
+
+// 创建并导出字典 Store,不启用持久化存储
+export const useDictStore = defineStore('dict', storeSetup, { persist: false })
diff --git a/frontend-pc/src/stores/useMenuBadgeStore.ts b/frontend-pc/src/stores/useMenuBadgeStore.ts
new file mode 100644
index 0000000..dfddea5
--- /dev/null
+++ b/frontend-pc/src/stores/useMenuBadgeStore.ts
@@ -0,0 +1,34 @@
+import type { BadgeInstance } from '@arco-design/web-vue'
+import { defineStore } from 'pinia'
+
+const storeSetup = () => {
+ // 菜单角标
+ const badgeMap: Record = reactive({
+ '/crud/index': 6,
+ '/form/base': 3,
+ '/form/step': 5,
+ '/table/dialog': 8
+ })
+
+ // 菜单角标属性
+ const BadgePropsMap: Record = {
+ '/crud/index': {
+ status: 'success'
+ },
+ '/form/base': {
+ style: 'transform: scale(0.8);'
+ },
+ '/form/step': {
+ style: { transform: 'scale(0.8)' },
+ class: 'g-badge-primary'
+ },
+ '/table/dialog': {
+ style: { transform: 'scale(0.8)' },
+ class: 'g-badge-success'
+ }
+ }
+
+ return { badgeMap, BadgePropsMap }
+}
+
+export const useMenuBadgeStore = defineStore('menu-badge', storeSetup)
diff --git a/frontend-pc/src/stores/useRouteStore.ts b/frontend-pc/src/stores/useRouteStore.ts
new file mode 100644
index 0000000..1c19d82
--- /dev/null
+++ b/frontend-pc/src/stores/useRouteStore.ts
@@ -0,0 +1,177 @@
+/**
+ * @file 路由状态管理模块
+ * @description 处理动态路由的加载、格式化和状态管理
+ */
+
+import type { RouteRecordRaw } from 'vue-router'
+import type { UserRouteItem } from '@/apis/user'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { mapTree } from 'xe-utils'
+import { constantRoutes } from '@/router'
+import { transformPathToName } from '@/utils'
+
+/**
+ * 布局组件
+ * 用于页面整体布局结构
+ */
+const Layout = () => import('@/layout/index.vue')
+
+/**
+ * 视图模块映射
+ * 自动导入 views 目录下的所有 .vue 文件
+ * 用于动态加载路由组件
+ */
+const modules = import.meta.glob('@/views/**/*.vue')
+
+/**
+ * 组件路径映射缓存
+ * 将视图路径映射到对应的模块加载函数,避免每次遍历查找
+ * key: 视图路径(如 'system/user/index')
+ * value: 模块加载函数
+ */
+const viewPathMap = new Map Promise>()
+
+/**
+ * 初始化组件路径映射
+ * @description 在模块加载时构建路径映射表,提升查找效率
+ */
+function initViewPathMap() {
+ if (viewPathMap.size > 0) return // 已初始化则跳过
+
+ for (const path in modules) {
+ // 提取视图路径:从 'src/views/system/user/index.vue' 提取 'system/user/index'
+ const dir = path.split('views/')[1]?.split('.vue')[0]
+ if (dir) {
+ viewPathMap.set(dir, () => modules[path]())
+ }
+ }
+}
+
+// 初始化路径映射
+initViewPathMap()
+
+/**
+ * 加载视图组件
+ * @description 根据路径加载对应的视图组件,使用 Map 缓存提升查找效率
+ * @param {string} view - 组件路径(相对于 views 目录)
+ * @returns {Function|undefined} 返回组件的加载函数或 undefined
+ * @example
+ * loadView('system/user/index')
+ */
+export const loadView = (view: string) => {
+ return viewPathMap.get(view)
+}
+
+/**
+ * 转换组件标识为实际组件
+ * @description 将字符串类型的组件标识转换为实际的组件
+ * @param {string} component - 组件标识
+ * @returns {Function} 返回组件的加载函数
+ * @example
+ * transformComponentView('Layout')
+ * transformComponentView('system/user/index')
+ */
+const transformComponentView = (component: string) => {
+ if (component === 'Layout') {
+ return Layout as never
+ } else {
+ return loadView(component) as never
+ }
+}
+
+/**
+ * 格式化异步路由配置
+ * @description 处理后端返回的菜单数据,转换为路由配置
+ * @param {MenuItem[]} menus - 后端返回的菜单数据(已经过权限过滤)
+ * @returns {RouteRecordRaw[]} 返回格式化后的路由配置数组
+ *
+ * @remarks
+ * 1. 对菜单数据进行排序(根据 sort 字段)
+ * 2. 将字符串类型的组件标识转换为实际组件
+ * 3. 处理路由元数据(meta)
+ *
+ * @example
+ * const routes = formatAsyncRoutes([
+ * {
+ * path: '/system',
+ * component: 'Layout',
+ * children: [...]
+ * }
+ * ])
+ */
+const formatAsyncRoutes = (menus: UserRouteItem[]) => {
+ if (!menus.length) return []
+
+ // 对顶层菜单进行排序
+ menus.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0))
+
+ // 使用 mapTree 递归处理菜单树
+ const routes = mapTree(menus, (item) => {
+ // 对子菜单进行排序
+ if (item.children && item.children.length) {
+ item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0))
+ }
+
+ // 转换为路由配置
+ return {
+ path: item.path,
+ name: transformPathToName(item.path),
+ component: item.component ? transformComponentView(item.component) : undefined,
+ redirect: item.redirect,
+ meta: {
+ hidden: item.hidden, // 是否在菜单中隐藏
+ keepAlive: item.keepAlive, // 是否缓存该路由
+ title: item.title, // 路由标题,用于显示在菜单和标签页
+ icon: item.icon, // 图标
+ affix: item.affix, // 是否固定在标签页
+ breadcrumb: item.breadcrumb, // 是否显示在面包屑
+ showInTabs: item.showInTabs, // 是否显示在标签页
+ activeMenu: item.activeMenu, // 激活的菜单项
+ alwaysShow: item.alwaysShow // 是否总是显示
+ }
+ }
+ })
+ return routes as RouteRecordRaw[]
+}
+
+/**
+ * 路由状态管理的核心逻辑
+ * @description 管理路由相关的状态和操作
+ */
+const storeSetup = () => {
+ /**
+ * 所有路由配置
+ * 包含常驻路由和动态路由
+ */
+ const routes = ref([])
+
+ /**
+ * 动态路由配置
+ * 根据用户权限动态生成的路由
+ */
+ const asyncRoutes = ref([])
+
+ /**
+ * 合并路由配置
+ * @description 将动态路由与常驻路由合并
+ * @param {RouteRecordRaw[]} data - 动态路由配置
+ */
+ const setRoutes = (data: UserRouteItem[]) => {
+ const formattedRoutes = formatAsyncRoutes(data)
+ routes.value = constantRoutes.concat(formattedRoutes)
+ asyncRoutes.value = formattedRoutes
+ }
+
+ return {
+ routes,
+ asyncRoutes,
+ setRoutes
+ }
+}
+
+/**
+ * 路由状态管理 Store
+ * @description 创建路由相关的状态管理 store,启用持久化存储
+ */
+export const useRouteStore = defineStore('route', storeSetup, { persist: true })
diff --git a/frontend-pc/src/stores/useTabsStore.ts b/frontend-pc/src/stores/useTabsStore.ts
new file mode 100644
index 0000000..4165374
--- /dev/null
+++ b/frontend-pc/src/stores/useTabsStore.ts
@@ -0,0 +1,250 @@
+/**
+ * @file 标签页状态管理模块
+ * @description 处理标签页(Tabs)的状态管理,包括页签的增删改查和缓存控制
+ */
+
+import type { RouteLocationNormalized, RouteRecordName } from 'vue-router'
+import { defineStore } from 'pinia'
+import { nextTick, ref } from 'vue'
+import { useRouter } from 'vue-router'
+
+/**
+ * 标签页 Store 的核心设置逻辑
+ * @description 管理标签页相关的状态和操作
+ */
+const storeSetup = () => {
+ const router = useRouter()
+
+ /**
+ * 标签页列表
+ * 存储当前打开的所有标签页
+ */
+ const tabList = ref([])
+
+ /**
+ * 缓存组件列表
+ * 存储需要被 keep-alive 缓存的组件名称
+ */
+ const cacheList = ref([])
+
+ /**
+ * 页面重载标志
+ * 用于控制页面的重新加载
+ */
+ const reloadFlag = ref(true)
+
+ /**
+ * 判断是否为首页
+ */
+ const isHomePath = (path: string) => path === '/' || path === '/home'
+
+ /**
+ * 获取所有固定标签页
+ * @description 从路由中筛选出所有 affix: true 的固定标签页,首页排在最前面
+ */
+ const getAffixTabs = (): RouteLocationNormalized[] => {
+ const tabs = router
+ .getRoutes()
+ .filter((route) => route.meta?.affix)
+ .map((route) => route as unknown as RouteLocationNormalized)
+ // 首页排在最前面
+ tabs.sort((a, b) => Number(isHomePath(b.path)) - Number(isHomePath(a.path)))
+ return tabs
+ }
+
+ /**
+ * 更新缓存列表
+ */
+ const updateCache = (name: RouteRecordName | null | undefined, keepAlive: boolean | undefined) => {
+ if (!name) return
+ const hasCache = cacheList.value.includes(name)
+ if (keepAlive && !hasCache) {
+ cacheList.value.push(name)
+ } else if (!keepAlive && hasCache) {
+ const index = cacheList.value.findIndex((n) => n === name)
+ if (index >= 0) cacheList.value.splice(index, 1)
+ }
+ }
+
+ /**
+ * 添加标签页
+ * @description 添加新的标签页,如果已存在则更新,同时处理缓存
+ */
+ const addTabItem = (item: RouteLocationNormalized) => {
+ const index = tabList.value.findIndex((i) => i.path === item.path)
+
+ if (index >= 0) {
+ // 更新已存在的标签页
+ if (tabList.value[index].fullPath !== item.fullPath) {
+ tabList.value[index] = item
+ }
+ updateCache(item.name, item.meta?.keepAlive)
+ } else if (item.meta?.showInTabs ?? true) {
+ // 添加新标签页
+ tabList.value.push(item)
+ updateCache(item.name, item.meta?.keepAlive)
+ }
+ }
+
+ /**
+ * 设置标签页标题
+ */
+ const setTabTitle = (title: string): boolean => {
+ if (!title) return false
+ const route = router.currentRoute.value
+ const path = route?.fullPath || route.path
+ const tab = tabList.value.find((i) => i.fullPath === path)
+ if (tab) {
+ tab.meta.title = title
+ return true
+ }
+ return false
+ }
+
+ /**
+ * 删除标签页并清理缓存
+ */
+ const removeTabByIndex = (index: number) => {
+ if (index < 0 || index >= tabList.value.length) return
+ const item = tabList.value[index]
+ updateCache(item.name, false) // 删除缓存
+ tabList.value.splice(index, 1)
+ }
+
+ /**
+ * 跳转到指定标签页
+ */
+ const jumpToTab = (path: string) => {
+ const tab = tabList.value.find((t) => t.path === path) ?? tabList.value[0]
+ if (tab) router.push(tab.fullPath || tab.path)
+ }
+
+ /**
+ * 获取固定标签页的缓存列表
+ */
+ const getAffixCacheList = (tabs: RouteLocationNormalized[]) => {
+ return tabs.filter((tab) => tab.name && tab.meta?.keepAlive).map((tab) => tab.name!)
+ }
+
+ /**
+ * 统一关闭标签页方法
+ * @description 根据类型关闭标签页:left(左侧)、right(右侧)、other(其他)、all(全部)、current(当前)
+ */
+ const close = (type: string, path?: string) => {
+ const currentPath = router.currentRoute.value.path
+ const targetPath = path || currentPath
+ const targetIndex = tabList.value.findIndex((i) => i.path === targetPath)
+
+ // 关闭所有
+ if (type === 'all') {
+ const affixTabs = getAffixTabs()
+ tabList.value = affixTabs
+ cacheList.value = getAffixCacheList(affixTabs)
+ router.push('/')
+ return
+ }
+
+ // 关闭当前
+ if (type === 'current') {
+ if (targetIndex < 0 || tabList.value[targetIndex].meta?.affix) return
+ removeTabByIndex(targetIndex)
+ if (currentPath === targetPath && tabList.value.length) {
+ jumpToTab(tabList.value[tabList.value.length - 1].path)
+ }
+ return
+ }
+
+ if (targetIndex < 0) return
+
+ // 关闭左侧
+ if (type === 'left') {
+ for (let i = targetIndex - 1; i >= 0; i--) {
+ if (!tabList.value[i].meta?.affix) removeTabByIndex(i)
+ }
+ return
+ }
+
+ // 关闭右侧
+ if (type === 'right') {
+ const currentIndex = tabList.value.findIndex((t) => t.path === currentPath)
+ const willDeleteCurrent = currentIndex > targetIndex && !tabList.value[currentIndex]?.meta?.affix
+
+ for (let i = tabList.value.length - 1; i > targetIndex; i--) {
+ if (!tabList.value[i].meta?.affix) removeTabByIndex(i)
+ }
+
+ if (willDeleteCurrent && tabList.value.length) {
+ jumpToTab(tabList.value[targetIndex].path)
+ }
+ return
+ }
+
+ // 关闭其他
+ if (type === 'other') {
+ for (let i = tabList.value.length - 1; i >= 0; i--) {
+ const item = tabList.value[i]
+ if (item.path !== targetPath && !item.meta?.affix) {
+ removeTabByIndex(i)
+ }
+ }
+ const currentExists = tabList.value.some((t) => t.path === currentPath)
+ if (!currentExists && tabList.value.length) {
+ jumpToTab(targetPath)
+ }
+ }
+ }
+
+ /**
+ * 重置标签页状态
+ * @description 清空标签页和缓存列表,保留固定标签页及其缓存
+ */
+ const reset = () => {
+ const affixTabs = getAffixTabs()
+ tabList.value = affixTabs
+ cacheList.value = getAffixCacheList(affixTabs)
+ }
+
+ /**
+ * 初始化标签页
+ * @description 初始化标签页状态,如果没有非固定标签页则重置
+ */
+ const init = () => {
+ if (tabList.value.some((i) => !i?.meta?.affix)) return
+ reset()
+ }
+
+ /**
+ * 重新加载当前页面
+ * @description 清除当前页面的缓存并重新加载
+ */
+ const reloadPage = () => {
+ const route = router.currentRoute.value
+ if (!route.name) return
+
+ updateCache(route.name, false) // 删除缓存
+ reloadFlag.value = false
+
+ nextTick(() => {
+ reloadFlag.value = true
+ updateCache(route.name, route.meta?.keepAlive) // 重新添加缓存
+ })
+ }
+
+ return {
+ tabList,
+ cacheList,
+ reloadFlag,
+ addTabItem,
+ setTabTitle,
+ close,
+ reset,
+ init,
+ reloadPage
+ }
+}
+
+/**
+ * 标签页状态管理 Store
+ * @description 创建标签页相关的状态管理 store,使用 sessionStorage 进行持久化存储
+ */
+export const useTabsStore = defineStore('tabs', storeSetup, { persist: true })
diff --git a/frontend-pc/src/stores/useUserStore.ts b/frontend-pc/src/stores/useUserStore.ts
new file mode 100644
index 0000000..81b3578
--- /dev/null
+++ b/frontend-pc/src/stores/useUserStore.ts
@@ -0,0 +1,164 @@
+/** @file 用户状态管理模块 - 处理用户登录、权限和信息管理 */
+
+import type { UserInfo } from '@/apis/user'
+import type { LoginVO } from '@/apis/xt'
+import { defineStore } from 'pinia'
+import { computed, reactive, ref } from 'vue'
+import { useRouter } from 'vue-router'
+import { login as loginApi, logout as logoutApi, getMe } from '@/apis/xt'
+import { buildXtRoutes } from '@/config/xt-routes'
+import { resetRouter } from '@/router'
+import { clearToken, getToken, setToken } from '@/utils/auth'
+import { isHttp } from '@/utils/validate'
+import { useRouteStore } from './useRouteStore'
+
+/** 登录参数:password 必须是 RSA 加密后的密文(见 @/utils/rsa) */
+type LoginParams = Parameters[0]
+
+/**
+ * 用户 Store 的核心设置逻辑
+ *
+ * 与模板原版的差别只有数据来源:模板调 `/user/login` 等 mock 接口,
+ * 这里改调本项目的 `/auth/login`(《03-接口设计》§5)。
+ * 内部状态形状保持不变,`v-hasPerm` 指令、布局、标签页都不用改。
+ */
+const storeSetup = () => {
+ const routeStore = useRouteStore()
+ const router = useRouter()
+
+ /** 用户基本信息(形状对齐模板的 UserInfo) */
+ const userInfo: UserInfo = reactive({
+ id: '',
+ nickname: '',
+ avatar: '',
+ roles: [],
+ permissions: []
+ })
+
+ /** 后端返回的原始档案,用于展示租户名等模板没有的字段 */
+ const profile = ref(null)
+
+ /** 菜单键集合(决定左侧导航) */
+ const menuKeys = ref([])
+
+ /** 用户昵称 */
+ const name = computed(() => userInfo.nickname)
+ /** 用户头像 */
+ const avatar = computed(() => userInfo.avatar)
+ /** 用户角色列表 */
+ const roles = computed(() => userInfo.roles)
+ /** 用户权限列表 */
+ const permissions = computed(() => userInfo.permissions)
+ /** 用户令牌 */
+ const token = ref(getToken() || '')
+ /** 所属租户名(侧栏/顶栏展示用) */
+ const tenantName = computed(() => profile.value?.tenant?.tenantName ?? '')
+ /** 主角色中文名 */
+ const roleLabel = computed(() => profile.value?.roleCodes?.[0] ?? '')
+
+ /** 重置用户令牌 - 清除令牌并重置路由权限标志 */
+ const resetToken = () => {
+ token.value = ''
+ clearToken()
+ }
+
+ /** 把后端返回的 LoginVO 落到模板内部的状态上 */
+ const applyProfile = (vo: LoginVO) => {
+ profile.value = vo
+ userInfo.id = String(vo.user?.id ?? '')
+ userInfo.nickname = vo.user?.realName ?? ''
+ userInfo.avatar = vo.user?.avatarUrl ?? ''
+ userInfo.roles = vo.roleCodes ?? []
+ userInfo.permissions = vo.permissions ?? []
+ menuKeys.value = vo.menus ?? []
+ }
+
+ /**
+ * 用户登录
+ * @param params 含 RSA 加密后的密码
+ */
+ const login = async (params: LoginParams): Promise => {
+ const res = await loginApi(params)
+ const vo = res.data
+ if (!vo?.token) {
+ throw new Error('服务端未返回 token')
+ }
+ setToken(vo.token)
+ token.value = vo.token
+ // 登录响应已带 user/tenant/roleCodes/menus/permissions,直接落库,省一次 /auth/me 往返
+ applyProfile(vo)
+ }
+
+ /** 用户退出 */
+ const logout = async (): Promise => {
+ try {
+ await logoutApi()
+ } catch {
+ // 登出失败也要清本地状态:否则用户会卡在"点了登出但还是登录着"
+ } finally {
+ token.value = ''
+ resetToken()
+ resetRouter()
+ profile.value = null
+ menuKeys.value = []
+ }
+ return true
+ }
+
+ /** 获取用户信息(刷新页面后靠它恢复状态) */
+ const fetchUserInfo = async (): Promise => {
+ const res = await getMe()
+ applyProfile(res.data)
+ return true
+ }
+
+ /**
+ * 生成动态路由
+ *
+ * 模板原版是直接取后端下发的路由树;本项目后端只下发**菜单键**,
+ * 所以这里在本地路由表(@/config/xt-routes)上做一次过滤。
+ * 这样"有哪些页面"由前端代码掌控,"谁能看"由后端掌控,两边职责清晰。
+ */
+ const generateRoutes = async (): Promise => {
+ // 已登录但刷新过页面时,profile 是空的,先补回来
+ if (!menuKeys.value.length) {
+ await fetchUserInfo()
+ }
+ routeStore.setRoutes(buildXtRoutes(menuKeys.value))
+ routeStore.asyncRoutes.forEach((item) => {
+ if (!isHttp(item.path)) {
+ router.addRoute(item)
+ }
+ })
+ return true
+ }
+
+ return {
+ userInfo,
+ profile,
+ menuKeys,
+ name,
+ avatar,
+ roles,
+ permissions,
+ token,
+ tenantName,
+ roleLabel,
+ login,
+ logout,
+ fetchUserInfo,
+ resetToken,
+ generateRoutes
+ }
+}
+
+/**
+ * 用户状态管理 Store
+ * @description 创建用户相关的状态管理 store,使用 localStorage 进行持久化存储
+ */
+export const useUserStore = defineStore('user', storeSetup, {
+ persist: {
+ pick: ['token'],
+ storage: localStorage
+ }
+})
diff --git a/frontend-pc/src/styles/animated.scss b/frontend-pc/src/styles/animated.scss
new file mode 100644
index 0000000..168cfd0
--- /dev/null
+++ b/frontend-pc/src/styles/animated.scss
@@ -0,0 +1,23 @@
+@keyframes fade-up {
+ 0% {
+ opacity: 0;
+ transform: translateY(60px);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@for $i from 0 through 12 {
+ .animated-fade-up-#{$i}
+
+ {
+ opacity: 0;
+ animation-name: fade-up;
+ animation-duration: 0.5s;
+ animation-delay: calc($i/10) + s;
+ animation-fill-mode: forwards;
+ }
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-alert.less b/frontend-pc/src/styles/arco-ui/a-alert.less
new file mode 100644
index 0000000..c77887d
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-alert.less
@@ -0,0 +1,3 @@
+.arco-alert {
+ flex-shrink: 0;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-card.less b/frontend-pc/src/styles/arco-ui/a-card.less
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-card.less
@@ -0,0 +1 @@
+
diff --git a/frontend-pc/src/styles/arco-ui/a-color-picker.less b/frontend-pc/src/styles/arco-ui/a-color-picker.less
new file mode 100644
index 0000000..e363ca7
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-color-picker.less
@@ -0,0 +1,19 @@
+.arco-color-picker-panel .arco-color-picker-panel-control .arco-color-picker-input-wrapper .arco-select-view,
+.arco-color-picker-panel .arco-color-picker-panel-control .arco-color-picker-input-wrapper .arco-input-wrapper {
+ padding-right: 0;
+}
+
+.arco-color-picker-panel .arco-color-picker-input-alpha {
+ flex: none;
+ width: 70px;
+
+ .arco-input-number-step {
+ width: 20px;
+ margin-left: 4px;
+ }
+}
+
+.arco-color-picker-panel .arco-color-picker-input-alpha:not(.arco-input-disabled):not(.arco-input-outer-disabled):hover .arco-input-number-suffix,
+.arco-color-picker-panel .arco-color-picker-input-alpha:not(.arco-input-disabled):not(.arco-input-outer-disabled):focus-within .arco-input-number-suffix {
+ opacity: 1;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-descriptions.less b/frontend-pc/src/styles/arco-ui/a-descriptions.less
new file mode 100644
index 0000000..060f7d4
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-descriptions.less
@@ -0,0 +1,11 @@
+.arco-descriptions-layout-inline-horizontal {
+ &.arco-descriptions-size-medium {
+ .arco-descriptions-item {
+ padding-bottom: 8px;
+ }
+ }
+}
+
+.arco-descriptions-layout-inline-horizontal .arco-descriptions-item-label-inline {
+ margin-right: 10px;
+}
diff --git a/frontend-pc/src/styles/arco-ui/a-form.less b/frontend-pc/src/styles/arco-ui/a-form.less
new file mode 100644
index 0000000..ad79db4
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-form.less
@@ -0,0 +1,28 @@
+// 清除默认过渡动画
+.form-blink-enter-from,
+.form-blink-appear-from {
+ opacity: inherit;
+}
+.form-blink-enter-to,
+.form-blink-appear-to {
+ opacity: inherit;
+}
+.form-blink-enter-active,
+.form-blink-appear-active {
+ transition: none;
+ animation: none;
+}
+
+// 重设过渡动画
+.form-blink-enter-active,
+.form-blink-leave-active {
+ opacity: 1;
+ transform: scaleY(1);
+ transition: transform 0.3s cubic-bezier(.23, 1, .32, 1), opacity 0.1s cubic-bezier(.23, 1, .32, 1);
+ transform-origin: center top;
+}
+.form-blink-enter-from,
+.form-blink-leave-active {
+ opacity: 0;
+ transform: scaleY(0);
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-input-number.less b/frontend-pc/src/styles/arco-ui/a-input-number.less
new file mode 100644
index 0000000..c16265a
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-input-number.less
@@ -0,0 +1,70 @@
+// 线性主题优化
+.arco-input-number.arco-input-number-mode-button {
+ .arco-input-prepend {
+ .arco-btn {
+ border-right: 0;
+ }
+ }
+
+ .arco-input-append {
+ .arco-btn {
+ border-left: 0;
+ }
+ }
+}
+
+.arco-input-number-mode-embed {
+ padding-right: 0;
+ overflow: hidden;
+
+ &.arco-input-wrapper:not(.arco-input-focus) .arco-input-number-step-button:not(.arco-input-number-step-button-disabled):hover {
+ background-color: var(--color-fill-2);
+ }
+
+ .arco-input-number-step {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ position: static;
+ display: flex;
+ flex-direction: column;
+ width: 34px;
+ height: 100%;
+ opacity: 1;
+ visibility: visible;
+ transform: none;
+ border-left: 1px solid var(--color-border-2);
+ }
+
+ .arco-input-number-step-button {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: auto;
+ min-height: 0;
+ border-left: 1px solid var(--color-border-2);
+
+ &:not(.arco-input-number-step-button-disabled) {
+ &:hover {
+ .arco-icon {
+ color: rgb(var(--primary-6));
+ }
+ }
+ }
+
+ &:hover,
+ &:active {
+ background-color: var(--color-fill-2);
+ }
+ }
+
+ .arco-input-number-step-button+.arco-input-number-step-button {
+ border-top: 1px solid var(--color-border-2);
+ }
+
+ .arco-input-number-input {
+ padding-right: 34px;
+ }
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-input.less b/frontend-pc/src/styles/arco-ui/a-input.less
new file mode 100644
index 0000000..9d75d17
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-input.less
@@ -0,0 +1,7 @@
+// 线性主题优化
+.arco-input-prepend {
+ border-right: none;
+}
+.arco-input-append {
+ border-left: none;
+}
diff --git a/frontend-pc/src/styles/arco-ui/a-menu.less b/frontend-pc/src/styles/arco-ui/a-menu.less
new file mode 100644
index 0000000..911c5c4
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-menu.less
@@ -0,0 +1,18 @@
+.arco-trigger-menu-item.arco-trigger-menu-selected,
+.arco-trigger-menu-pop-header.arco-trigger-menu-selected {
+ color: rgb(var(--primary-6));
+}
+
+.arco-menu-icon-suffix {
+ transition: all 0.2s;
+}
+
+.arco-menu-horizontal .arco-menu-pop {
+ display: inline-flex;
+}
+
+.arco-menu-icon {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-message.less b/frontend-pc/src/styles/arco-ui/a-message.less
new file mode 100644
index 0000000..a8a04f7
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-message.less
@@ -0,0 +1,28 @@
+.fade-message-enter-from {
+ opacity: 0;
+ transform: translate3d(0, -100%, 0);
+}
+
+.fade-message-enter-to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+}
+
+.fade-message-enter-active,
+.fade-message-appear-active {
+ transition: opacity @transition-duration-3, transform @transition-duration-4;
+}
+
+.fade-message-leave-from {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+}
+
+.fade-message-leave-to {
+ opacity: 0;
+ transform: translate3d(0, -100%, 0);
+}
+
+.fade-message-leave-active {
+ position: absolute;
+}
diff --git a/frontend-pc/src/styles/arco-ui/a-modal.less b/frontend-pc/src/styles/arco-ui/a-modal.less
new file mode 100644
index 0000000..448cc68
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-modal.less
@@ -0,0 +1,44 @@
+.arco-modal-title .arco-icon-exclamation-circle-fill {
+ font-size: 24px;
+}
+
+.arco-modal-title-icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 4px;
+}
+
+.arco-modal-simple {
+ width: 90%;
+ max-width: 320px;
+ padding: 20px;
+ box-sizing: border-box;
+ border-radius: 20px;
+
+ .arco-modal-header {
+ margin-bottom: 0;
+
+ .arco-modal-title {
+ font-size: 18px;
+ }
+ }
+
+ .arco-modal-body {
+ padding: 20px 0;
+ font-size: 16px;
+ text-align: center;
+ }
+
+ .arco-modal-footer {
+ margin-top: 0;
+ display: flex;
+
+ >.arco-btn {
+ flex: 1;
+ height: 38px;
+ border-radius: 100px;
+ font-weight: bold;
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-page-header.less b/frontend-pc/src/styles/arco-ui/a-page-header.less
new file mode 100644
index 0000000..70c8da0
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-page-header.less
@@ -0,0 +1,6 @@
+.arco-page-header {
+ padding: 8px 0;
+ background: var(--color-bg-1);
+ border-bottom: 1px solid var(--color-border-2);
+ box-sizing: border-box;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-pagination.less b/frontend-pc/src/styles/arco-ui/a-pagination.less
new file mode 100644
index 0000000..25661fd
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-pagination.less
@@ -0,0 +1,41 @@
+// 分页组件
+.arco-pagination-item {
+ padding: 0 6px;
+ border: 1px solid var(--color-border-3);
+ background-color: var(--color-bg-2);
+ border-radius: var(--border-radius-small);
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ transition: none;
+ box-sizing: border-box;
+ &:hover {
+ color: rgb(var(--primary-6));
+ border: 1px solid rgb(var(--primary-6));
+ background-color: rgba(var(--primary-6), 0.1);
+ }
+ &:active {
+ color: #fff;
+ background-color: rgb(var(--primary-6));
+ border: 1px solid rgb(var(--primary-6));
+ }
+}
+
+.arco-pagination-item-active {
+ color: #fff;
+ background-color: rgb(var(--primary-6));
+ border: 1px solid rgb(var(--primary-6));
+ &:hover {
+ color: #fff;
+ background-color: rgb(var(--primary-6));
+ border: 1px solid rgb(var(--primary-6));
+ }
+}
+
+.arco-pagination .arco-pagination-item-previous.arco-pagination-item-disabled,
+.arco-pagination .arco-pagination-item-next.arco-pagination-item-disabled {
+ border-color: var(--color-border-2);
+ &:hover {
+ border-color: var(--color-border-2);
+ }
+}
diff --git a/frontend-pc/src/styles/arco-ui/a-picker.less b/frontend-pc/src/styles/arco-ui/a-picker.less
new file mode 100644
index 0000000..5c5aaed
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-picker.less
@@ -0,0 +1,3 @@
+.arco-picker {
+ width: 100%;
+}
diff --git a/frontend-pc/src/styles/arco-ui/a-radio.less b/frontend-pc/src/styles/arco-ui/a-radio.less
new file mode 100644
index 0000000..430e2a0
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-radio.less
@@ -0,0 +1,3 @@
+.arco-radio-button-content {
+ white-space: nowrap;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-scrollbar.less b/frontend-pc/src/styles/arco-ui/a-scrollbar.less
new file mode 100644
index 0000000..ba3a990
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-scrollbar.less
@@ -0,0 +1,21 @@
+.arco-scrollbar-track-direction-vertical {
+ width: 8px;
+}
+
+.arco-scrollbar-thumb-direction-vertical .arco-scrollbar-thumb-bar {
+ width: 6px;
+ margin: 0 1px;
+}
+
+.arco-scrollbar-track-direction-horizontal {
+ height: 8px;
+}
+
+.arco-scrollbar-thumb-direction-horizontal .arco-scrollbar-thumb-bar {
+ height: 6px;
+ margin: 1px 0;
+}
+
+.arco-scrollbar-thumb-bar {
+ background-color: var(--color-fill-3);
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-space.less b/frontend-pc/src/styles/arco-ui/a-space.less
new file mode 100644
index 0000000..e69de29
diff --git a/frontend-pc/src/styles/arco-ui/a-steps.less b/frontend-pc/src/styles/arco-ui/a-steps.less
new file mode 100644
index 0000000..2b22702
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-steps.less
@@ -0,0 +1,5 @@
+.arco-steps-item-process .arco-steps-icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-switch.less b/frontend-pc/src/styles/arco-ui/a-switch.less
new file mode 100644
index 0000000..1dbaa77
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-switch.less
@@ -0,0 +1,7 @@
+.arco-switch-type-round.arco-switch-small {
+ border-radius: 3px;
+}
+
+.arco-switch-type-round.arco-switch-small .arco-switch-handle {
+ border-radius: 2px;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-table.less b/frontend-pc/src/styles/arco-ui/a-table.less
new file mode 100644
index 0000000..6a38f3b
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-table.less
@@ -0,0 +1,9 @@
+.arco-table-th-title {
+ font-size: 13px;
+ font-weight: 600;
+}
+
+.arco-table-container {
+ border-radius: var(--border-radius-small);
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-tabs.less b/frontend-pc/src/styles/arco-ui/a-tabs.less
new file mode 100644
index 0000000..895b36a
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-tabs.less
@@ -0,0 +1,17 @@
+.arco-tabs-nav-extra:not(:empty) {
+ margin-right: var(--padding);
+}
+
+.arco-tabs {
+ background: var(--color-bg-1);
+}
+
+.arco-tabs-nav-type-rounded .arco-tabs-tab {
+ margin: 4px;
+ padding: 2px 12px;
+}
+
+.arco-tabs-nav-type-card .arco-tabs-tab:first-child,
+.arco-tabs-nav-type-card .arco-tabs-tab:last-child {
+ border-radius: 0;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/a-tree.less b/frontend-pc/src/styles/arco-ui/a-tree.less
new file mode 100644
index 0000000..ef420f7
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/a-tree.less
@@ -0,0 +1,23 @@
+.arco-tree-show-line .arco-tree-node-is-leaf:not(.arco-tree-node-is-tail) .arco-tree-node-indent::after,
+.arco-tree-show-line .arco-tree-node-indent-block::before {
+ transform: translateX(0);
+ border-color: var(--color-border-3);
+ flex-shrink: 0;
+}
+
+.arco-tree-node-minus-icon,
+.arco-tree-node-plus-icon {
+ border: 1px solid var(--color-border-3);
+ box-sizing: border-box;
+ border-radius: 4px;
+}
+
+.arco-tree-node-switcher-icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.arco-tree-node-title {
+ white-space: nowrap;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/index.less b/frontend-pc/src/styles/arco-ui/index.less
new file mode 100644
index 0000000..bcea62e
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/index.less
@@ -0,0 +1,46 @@
+// Arco 的 LESS 变量(供本项目组件覆写时引用,如 @transition-duration-3)。
+// 这是纯变量文件,不输出任何 CSS —— 原本由被移除的 @arco-themes/vue-gi-demo 带入。
+@import '@arco-design/web-vue/es/style/theme/index.less';
+
+// 本项目主题:腾讯云 / TDesign 色板(覆盖 Arco 底层色板,上层自动跟随)
+@import './xt-theme.less';
+
+@import './a-alert.less';
+@import './a-card.less';
+@import './a-color-picker.less';
+@import './a-descriptions.less';
+@import './a-form.less';
+@import './a-input-number.less';
+@import './a-input.less';
+@import './a-menu.less';
+@import './a-message.less';
+@import './a-modal.less';
+@import './a-page-header.less';
+@import './a-pagination.less';
+@import './a-picker.less';
+@import './a-radio.less';
+@import './a-scrollbar.less';
+@import './a-space.less';
+@import './a-steps.less';
+@import './a-switch.less';
+@import './a-table.less';
+@import './a-tabs.less';
+@import './a-tree.less';
+
+.app-menu-dark:extend(body[arco-theme='dark']) {
+ --color-menu-dark-bg: #222222;
+}
+
+// 禁止文本复制
+.arco-link,
+.arco-menu-title,
+.arco-menu-item-inner,
+.arco-tabs-tab-title,
+.arco-breadcrumb-item,
+.arco-checkbox-label {
+ user-select: none;
+}
+
+.arco-menu .arco-menu-item {
+ display: inline-flex;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/arco-ui/xt-theme.less b/frontend-pc/src/styles/arco-ui/xt-theme.less
new file mode 100644
index 0000000..280d644
--- /dev/null
+++ b/frontend-pc/src/styles/arco-ui/xt-theme.less
@@ -0,0 +1,237 @@
+/**
+ * 腾讯云(TDesign)主题
+ * ================================================================
+ * 色值来源:TDesign 官方 light 主题 token
+ * https://github.com/Tencent/tdesign-common style/web/theme/_light.less
+ * 主色 = 腾讯蓝 #0052D9
+ *
+ * ⚠️ 格式坑(写错整个主题不生效,且不报错):
+ * Arco 的颜色变量是 **逗号分隔的 RGB 三元组**,不是 #RRGGBB。
+ * 定义:--arcoblue-6: 22,93,255;
+ * 消费:rgb(var(--primary-6))
+ * 写成 `--arcoblue-6: #0052d9;` 的话,`rgb(#0052d9)` 是非法值,
+ * 浏览器会静默丢弃该声明 —— 表现是"主题完全不生效",但控制台没有报错。
+ *
+ * 为什么只覆写底层色板就够了:
+ * Arco 的 --primary-N / --success-N / --warning-N / --danger-N / --link-N
+ * 分别是 --arcoblue-N / --green-N / --orange-N / --red-N 的别名,
+ * 而 --color-text-* / --color-border-* / --color-fill-* 又派生自 gray 色板。
+ * 所以换底层色板 = 整套主题跟着换,不用逐个组件去改。
+ *
+ * 索引对应关系(两套色板的「主色」下标不同,必须按角色映射,不能按下标硬对):
+ * Arco : -1 最浅底 · -5 hover · -6 主色 · -7 active · -8~-10 更深
+ * TD : -1 最浅底 · -7 hover · -8 主色 · -9 active · -10 最深
+ */
+
+/**
+ * ⚠️ 必须同时写 `:root, body`,只写 `:root` 是不生效的。
+ *
+ * Arco 把自己的色板定义在 **body** 选择器上(不是 :root)。
+ * CSS 自定义属性是**按元素解析**的:body 上的 `--arcoblue-6` 会盖掉
+ * 从 html(:root)继承下来的同名变量,哪怕我们的样式加载在后面。
+ * 所以覆盖必须落在同一层级(body)才能赢。
+ *
+ * 排查特征:`getComputedStyle(document.documentElement).getPropertyValue('--arcoblue-6')`
+ * 已经是新值,但按钮颜色没变 —— 就是这个原因。
+ */
+:root,
+body {
+ /* ── 品牌色:腾讯蓝 #0052D9 ──────────────────────────
+ TD brand: 1 #ecf2fe · 2 #d4e3fc · 3 #bbd3fb · 4 #96bbf8
+ 5 #699ef5 · 6 #4787f0 · 7 #266fe8 · 8 #0052d9(主)
+ 9 #0034b5 · 10 #001f97
+ Arco 9/10 由 TD 10 继续压暗推导(TD 官方只到 10 阶) */
+ --arcoblue-1: 236, 242, 254; /* TD brand-1 #ecf2fe */
+ --arcoblue-2: 212, 227, 252; /* TD brand-2 #d4e3fc */
+ --arcoblue-3: 187, 211, 251; /* TD brand-3 #bbd3fb */
+ --arcoblue-4: 150, 187, 248; /* TD brand-4 #96bbf8 */
+ --arcoblue-5: 71, 135, 240; /* TD brand-6 #4787f0 — Arco 的 -5 是 hover */
+ --arcoblue-6: 0, 82, 217; /* TD brand-8 #0052d9 ★ 主色 */
+ --arcoblue-7: 0, 52, 181; /* TD brand-9 #0034b5 — Arco 的 -7 是 active */
+ --arcoblue-8: 0, 31, 151; /* TD brand-10 #001f97 */
+ --arcoblue-9: 0, 22, 122; /* 推导:由 #001f97 再压暗 */
+ --arcoblue-10: 0, 18, 95; /* 推导 */
+
+ /* ── 成功色:TD success,主色 -5 #00a870 ───────────── */
+ --green-1: 232, 248, 242; /* TD success-1 #e8f8f2 */
+ --green-2: 188, 235, 220; /* TD success-2 #bcebdc */
+ --green-3: 133, 219, 190; /* TD success-3 #85dbbe */
+ --green-4: 103, 209, 173; /* 推导:TD 3↔4 之间,给 Arco -4 留一档 */
+ --green-5: 72, 199, 156; /* TD success-4 #48c79c — hover */
+ --green-6: 0, 168, 112; /* TD success-5 #00a870 ★ 主色 */
+ --green-7: 7, 141, 92; /* TD success-6 #078d5c — active */
+ --green-8: 6, 121, 69; /* TD success-7 #067945 */
+ --green-9: 5, 99, 52; /* TD success-8 #056334 */
+ --green-10: 4, 79, 42; /* TD success-9 #044f2a */
+
+ /* ── 警告色:TD warning,主色 -5 #ed7b2f ───────────── */
+ --orange-1: 254, 243, 230; /* TD warning-1 #fef3e6 */
+ --orange-2: 249, 224, 199; /* TD warning-2 #f9e0c7 */
+ --orange-3: 247, 199, 151; /* TD warning-3 #f7c797 */
+ --orange-4: 244, 182, 122; /* 推导:TD 3↔4 之间 */
+ --orange-5: 242, 153, 95; /* TD warning-4 #f2995f — hover */
+ --orange-6: 237, 123, 47; /* TD warning-5 #ed7b2f ★ 主色 */
+ --orange-7: 211, 90, 33; /* TD warning-6 #d35a21 — active */
+ --orange-8: 186, 67, 27; /* TD warning-7 #ba431b */
+ --orange-9: 158, 54, 16; /* TD warning-8 #9e3610 */
+ --orange-10: 132, 43, 11; /* TD warning-9 #842b0b */
+
+ /* ── 危险色:TD error,主色 -6 #e34d59(这一族与 Arco 下标一一对应)── */
+ --red-1: 253, 236, 238; /* TD error-1 #fdecee */
+ --red-2: 249, 215, 217; /* TD error-2 #f9d7d9 */
+ --red-3: 248, 185, 190; /* TD error-3 #f8b9be */
+ --red-4: 247, 141, 148; /* TD error-4 #f78d94 */
+ --red-5: 243, 109, 120; /* TD error-5 #f36d78 — hover */
+ --red-6: 227, 77, 89; /* TD error-6 #e34d59 ★ 主色 */
+ --red-7: 201, 53, 63; /* TD error-7 #c9353f — active */
+ --red-8: 177, 31, 38; /* TD error-8 #b11f26 */
+ --red-9: 149, 17, 20; /* TD error-9 #951114 */
+ --red-10: 104, 5, 6; /* TD error-10 #680506 */
+
+ /* ── 中性灰:TD gray(TD 有 14 阶,Arco 需要 10 阶,按明度对齐)── */
+ --gray-1: 243, 243, 243; /* TD gray-1 #f3f3f3 */
+ --gray-2: 231, 231, 231; /* TD gray-3 #e7e7e7 */
+ --gray-3: 220, 220, 220; /* TD gray-4 #dcdcdc */
+ --gray-4: 197, 197, 197; /* TD gray-5 #c5c5c5 */
+ --gray-5: 166, 166, 166; /* TD gray-6 #a6a6a6 */
+ --gray-6: 139, 139, 139; /* TD gray-7 #8b8b8b */
+ --gray-7: 119, 119, 119; /* TD gray-8 #777777 */
+ --gray-8: 94, 94, 94; /* TD gray-9 #5e5e5e */
+ --gray-9: 56, 56, 56; /* TD gray-11 #383838 */
+ --gray-10: 44, 44, 44; /* TD gray-12 #2c2c2c */
+
+ /* ── 文字:直接用 TD 的 rgba 写法,不经过 gray 色板 ──
+ (文字是最高频的视觉元素,不绕一层间接引用更可控) */
+ --color-text-1: rgba(0, 0, 0, 90%);
+ --color-text-2: rgba(0, 0, 0, 60%);
+ --color-text-3: rgba(0, 0, 0, 40%);
+ --color-text-4: rgba(0, 0, 0, 26%);
+
+ /* ── 描边:TD border-level-1 #e7e7e7 / level-2 #dcdcdc ── */
+ --color-border-1: #f3f3f3;
+ --color-border-2: #e7e7e7;
+ --color-border-3: #dcdcdc;
+ --color-border-4: #c5c5c5;
+
+ /* ── 填充 ─────────────────────────────────────────── */
+ --color-fill-1: #f3f3f3;
+ --color-fill-2: #eee;
+ --color-fill-3: #e7e7e7;
+ --color-fill-4: #dcdcdc;
+
+ /* ── 背景:页面 #f3f3f3、容器 #fff(TD 的 page / container)── */
+ --color-bg-1: #fff;
+ --color-bg-2: #fff;
+ --color-bg-3: #fff;
+ --color-bg-4: #fff;
+ --color-bg-5: #f3f3f3;
+ --color-bg-white: #fff;
+
+ /* ── 圆角:Arco 默认 2/4/8,调成更接近 TD 的 4/6/8 ── */
+ --border-radius-small: 4px;
+ --border-radius-medium: 6px;
+ --border-radius-large: 8px;
+
+ /* ── 主色 RGB 明文,供 rgb(var(--xt-primary)) 这类自定义场景用 ── */
+ --xt-primary: 0, 82, 217;
+ --xt-primary-hover: 71, 135, 240;
+ --xt-primary-active: 0, 52, 181;
+ --xt-success: 0, 168, 112;
+ --xt-warning: 237, 123, 47;
+ --xt-danger: 227, 77, 89;
+ --xt-page-bg: #f3f3f3;
+ --xt-container-bg: #fff;
+ --xt-text-1: rgba(0, 0, 0, 90%);
+ --xt-text-2: rgba(0, 0, 0, 60%);
+ --xt-text-3: rgba(0, 0, 0, 40%);
+
+ /* ────────────────────────────────────────────────────────────────
+ 语义别名:显式覆盖,不走 Arco 的 --xxx-N: var(--palette-N) 间接层。
+ 实测那一层在 body 上算出来的仍是 Arco 默认色,所以这里直接给值。
+ ──────────────────────────────────────────────────────────────── */
+
+ /* 品牌(腾讯蓝) */
+ --primary-1: 236, 242, 254;
+ --primary-2: 212, 227, 252;
+ --primary-3: 187, 211, 251;
+ --primary-4: 150, 187, 248;
+ --primary-5: 71, 135, 240;
+ --primary-6: 22,93,255;
+ --primary-7: 0, 52, 181;
+ --primary-8: 0, 31, 151;
+ --primary-9: 0, 22, 122;
+ --primary-10: 0, 18, 95;
+
+ /* 成功 */
+ --success-1: 232, 248, 242;
+ --success-2: 188, 235, 220;
+ --success-3: 133, 219, 190;
+ --success-4: 103, 209, 173;
+ --success-5: 72, 199, 156;
+ --success-6: 0, 168, 112;
+ --success-7: 7, 141, 92;
+ --success-8: 6, 121, 69;
+ --success-9: 5, 99, 52;
+ --success-10: 4, 79, 42;
+
+ /* 警告 */
+ --warning-1: 254, 243, 230;
+ --warning-2: 249, 224, 199;
+ --warning-3: 247, 199, 151;
+ --warning-4: 244, 182, 122;
+ --warning-5: 242, 153, 95;
+ --warning-6: 237, 123, 47;
+ --warning-7: 211, 90, 33;
+ --warning-8: 186, 67, 27;
+ --warning-9: 158, 54, 16;
+ --warning-10: 132, 43, 11;
+
+ /* 危险 */
+ --danger-1: 253, 236, 238;
+ --danger-2: 249, 215, 217;
+ --danger-3: 248, 185, 190;
+ --danger-4: 247, 141, 148;
+ --danger-5: 243, 109, 120;
+ --danger-6: 227, 77, 89;
+ --danger-7: 201, 53, 63;
+ --danger-8: 177, 31, 38;
+ --danger-9: 149, 17, 20;
+ --danger-10: 104, 5, 6;
+
+ /* 链接色跟随品牌 */
+ --link-1: 236, 242, 254;
+ --link-2: 212, 227, 252;
+ --link-3: 187, 211, 251;
+ --link-4: 150, 187, 248;
+ --link-5: 71, 135, 240;
+ --link-6: 22,93,255;
+ --link-7: 0, 52, 181;
+ --link-8: 0, 31, 151;
+ --link-9: 0, 22, 122;
+ --link-10: 0, 18, 95;
+
+ /* 中性色(--color-neutral-N 由它派生,文字/描边/填充都跟着走) */
+ --color-neutral-1: 243, 243, 243;
+ --color-neutral-2: 231, 231, 231;
+ --color-neutral-3: 220, 220, 220;
+ --color-neutral-4: 197, 197, 197;
+ --color-neutral-5: 166, 166, 166;
+ --color-neutral-6: 139, 139, 139;
+ --color-neutral-7: 119, 119, 119;
+ --color-neutral-8: 94, 94, 94;
+ --color-neutral-9: 56, 56, 56;
+ --color-neutral-10: 44, 44, 44;
+}
+
+/**
+ * 暗色模式下保持同一主色体系。
+ * Arco 通过 body[arco-theme='dark'] 切换,这里只覆盖品牌色与页面底色,
+ * 其余(文字/背景/描边)沿用 Arco 自己的暗色派生,避免手写一套不完整的暗色。
+ */
+body[arco-theme='dark'] {
+ --arcoblue-6: 64, 128, 255; /* 暗色下主色提亮,保证对比度 */
+ --arcoblue-5: 106, 161, 255;
+ --arcoblue-7: 22, 93, 255;
+ --xt-page-bg: #17171a;
+ --xt-container-bg: #232324;
+}
diff --git a/frontend-pc/src/styles/base.scss b/frontend-pc/src/styles/base.scss
new file mode 100644
index 0000000..179d0c1
--- /dev/null
+++ b/frontend-pc/src/styles/base.scss
@@ -0,0 +1,53 @@
+* {
+ padding: 0;
+ margin: 0;
+}
+
+html,
+body,
+#app {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+}
+
+html {
+ font-size: 14px;
+}
+
+body {
+ --margin: 14px;
+ --padding: 16px;
+ overflow: hidden;
+ // font-family: "helvetica neue", helvetica, "pingfang sc", "hiragino sans gb", "microsoft yahei", "\5fae\8f6f\96c5\9ed1", "arial/sans-serif";
+ color: var(--color-text-2);
+ // 页面底色:腾讯云 / TDesign 的 bg-color-page(--td-gray-color-1)
+ // 用变量而不是写死色值,暗色模式与后续换色都不用改这里
+ background-color: var(--xt-page-bg, #f3f3f3);
+ text-rendering: optimizelegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body[arco-theme='dark'] {
+ background-color: #17171a; // body背景颜色-深色模式(Arco 暗色底)
+}
+
+#app {
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+}
+
+ul {
+ margin-bottom: 0;
+ list-style: none !important;
+}
+
+a,
+a:focus,
+a:hover {
+ color: inherit;
+ text-decoration: none;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/css/transition.css b/frontend-pc/src/styles/css/transition.css
new file mode 100644
index 0000000..faa4a50
--- /dev/null
+++ b/frontend-pc/src/styles/css/transition.css
@@ -0,0 +1,163 @@
+/* fade */
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.3s ease-in-out;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+ opacity: 0;
+}
+
+/* fade-slide */
+.fade-slide-leave-active,
+.fade-slide-enter-active {
+ transition: all 0.3s;
+}
+
+.fade-slide-enter-from {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+
+.fade-slide-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+/* fade-bottom */
+.fade-bottom-enter-active,
+.fade-bottom-leave-active {
+ transition: opacity 0.25s, transform 0.3s;
+}
+
+.fade-bottom-enter-from {
+ opacity: 0;
+ transform: translateY(-10%);
+}
+
+.fade-bottom-leave-to {
+ opacity: 0;
+ transform: translateY(10%);
+}
+
+/* fade-scale */
+.fade-scale-leave-active,
+.fade-scale-enter-active {
+ transition: all 0.28s;
+}
+
+.fade-scale-enter-from {
+ opacity: 0;
+ transform: scale(1.2);
+}
+
+.fade-scale-leave-to {
+ opacity: 0;
+ transform: scale(0.8);
+}
+
+/* zoom-fade */
+.zoom-fade-enter-active,
+.zoom-fade-leave-active {
+ transition: transform 0.2s, opacity 0.3s ease-out;
+}
+
+.zoom-fade-enter-from {
+ opacity: 0;
+ transform: scale(0.92);
+}
+
+.zoom-fade-leave-to {
+ opacity: 0;
+ transform: scale(1.06);
+}
+
+/* zoom-out */
+.zoom-out-enter-active,
+.zoom-out-leave-active {
+ transition: opacity 0.1s ease-in-out, transform 0.15s ease-out;
+}
+
+.zoom-out-enter-from,
+.zoom-out-leave-to {
+ opacity: 0;
+ transform: scale(0);
+}
+
+/* elementUI 的 Dialog 动画 */
+@keyframes dialog-fade-in {
+ 0% {
+ opacity: 0;
+ transform: translate3d(0, -20px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes dialog-fade-out {
+ 0% {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: translate3d(0, -20px, 0);
+ }
+}
+
+@keyframes modal-fade-in {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes modal-fade-out {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+.el-dialog-enter-active {
+ animation: dialog-fade-in 0.3s;
+}
+
+.el-dialog-leave-active {
+ animation: dialog-fade-out 0.3s;
+}
+
+.el-mask-enter-active {
+ animation: modal-fade-in 0.3s;
+}
+
+.el-mask-leave-active {
+ animation: modal-fade-out 0.3s;
+}
+
+/* el-fade-bottom */
+.el-fade-enter-active,
+.el-fade-leave-active {
+ transition: all 0.3s;
+}
+
+.el-fade-enter-from {
+ opacity: 0;
+ transform: scale(1.1, 0.2);
+}
+
+.el-fade-leave-to {
+ opacity: 0;
+ transform: scale(1.1, 0.2);
+}
diff --git a/frontend-pc/src/styles/editor.scss b/frontend-pc/src/styles/editor.scss
new file mode 100644
index 0000000..ce5b384
--- /dev/null
+++ b/frontend-pc/src/styles/editor.scss
@@ -0,0 +1,37 @@
+body {
+ --w-e-textarea-bg-color: #fff;
+ --w-e-textarea-color: #333;
+ --w-e-textarea-border-color: #ccc;
+ --w-e-textarea-slight-border-color: #e8e8e8;
+ --w-e-textarea-slight-color: #d4d4d4;
+ --w-e-textarea-slight-bg-color: #f5f2f0;
+ --w-e-textarea-selected-border-color: #b4d5ff;
+ --w-e-textarea-handler-bg-color: #4290f7;
+ --w-e-toolbar-color: #595959;
+ --w-e-toolbar-bg-color: #fff;
+ --w-e-toolbar-active-color: #333;
+ --w-e-toolbar-active-bg-color: #f1f1f1;
+ --w-e-toolbar-disabled-color: #999;
+ --w-e-toolbar-border-color: #e8e8e8;
+ --w-e-modal-button-bg-color: #fafafa;
+ --w-e-modal-button-border-color: #d9d9d9;
+}
+
+body[arco-theme='dark'] {
+ --w-e-textarea-bg-color: var(--color-bg-2);
+ --w-e-textarea-color: var(--color-text-1);
+ --w-e-textarea-border-color: var(--color-border);
+ --w-e-textarea-slight-border-color: var(--color-border-2);
+ --w-e-textarea-slight-color: var(--color-text-4);
+ --w-e-textarea-slight-bg-color: var(--color-bg-2);
+ --w-e-textarea-selected-border-color: var(--color-primary-light-1);
+ --w-e-textarea-handler-bg-color: var(--color-primary-light-4);
+ --w-e-toolbar-color: #ccc;
+ --w-e-toolbar-bg-color: var(--color-bg-2);
+ --w-e-toolbar-active-color: #fff;
+ --w-e-toolbar-active-bg-color: var(--color-secondary-active);
+ --w-e-toolbar-disabled-color: var(--color-text-4);
+ --w-e-toolbar-border-color: var(--color-border);
+ --w-e-modal-button-bg-color: var(--color-fill-2);
+ --w-e-modal-button-border-color: var(--color-border);
+}
diff --git a/frontend-pc/src/styles/global.scss b/frontend-pc/src/styles/global.scss
new file mode 100644
index 0000000..0141919
--- /dev/null
+++ b/frontend-pc/src/styles/global.scss
@@ -0,0 +1,335 @@
+/* 全局样式 */
+@use './var.scss' as *;
+
+// hover按钮
+.g-hover-btn {
+ background-color: transparent;
+ border: 0 !important;
+
+ &:hover {
+ background: var(--color-secondary-hover) !important;
+ }
+
+ &:active {
+ background: var(--color-secondary-active) !important;
+ }
+}
+
+// 卡片组件,使body的高度自动撑满
+.g-card {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+
+ .arco-card-body {
+ box-sizing: border-box;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+ }
+}
+
+// 页面卡片,右侧有返回按钮
+.g-page-card {
+ box-sizing: border-box;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ margin: var(--margin);
+ overflow: hidden;
+
+ .arco-card-body {
+ box-sizing: border-box;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+ }
+}
+
+.g-gradient-bg {
+ background-image: linear-gradient(to right top, transparent, transparent,
+ color-mix(in oklab, rgb(var(--primary-6)) 12%, transparent),
+ color-mix(in oklab, rgb(var(--primary-6)) 12%, transparent));
+}
+
+// 卡片标题,标题左侧的伪类样式
+.g-card-title {
+ .arco-card-header-title::before {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ width: 4px;
+ height: 50%;
+ content: '';
+ background-color: rgb(var(--warning-5));
+ border-radius: 0 4px 4px 0;
+ transform: translateY(-50%);
+ }
+}
+
+// 使用场景,页面内容超出高度会自动滚动
+.g-page {
+ box-sizing: border-box;
+ flex: 1;
+ padding: var(--margin);
+ overflow-y: auto;
+}
+
+// 表格类名 高度自适应
+.g-table {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+
+ >.arco-spin {
+ height: auto;
+ max-height: 100%;
+ overflow: hidden;
+ }
+}
+
+// 表格类名,高度自适应,分页始终固定在最底部
+.g-full-table {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+
+ .arco-table-container {
+ flex: 1;
+ overflow: hidden;
+ }
+
+ .arco-table-scroll-y {
+ border-right: 1px solid var(--color-border-1);
+ }
+
+ .arco-table-tr .arco-table-td:last-child,
+ .arco-table-tr .arco-table-th:last-child {
+ border-right: none;
+ }
+
+ .arco-table-content {
+ .arco-scrollbar {
+ &:last-child {
+ flex: 1;
+
+ &:not(.arco-scrollbar-both) {
+ .arco-table-element {
+ border-bottom: 1px solid var(--color-border-1);
+ }
+ }
+ }
+ }
+ }
+}
+
+.g-full-tabs {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+
+ .arco-tabs-content {
+ flex: 1;
+ padding-top: 0;
+
+ .arco-tabs-content-list {
+ height: 100%;
+ }
+
+ .arco-tabs-pane {
+ height: 100%;
+ overflow-y: auto;
+ }
+ }
+}
+
+// 编辑表格,表头的必填红色星号
+.g-column-require {
+ .arco-table-th-title {
+ &::before {
+ padding-right: 2px;
+ color: red;
+ content: '*';
+ }
+ }
+}
+
+.g-full-column {
+ box-sizing: border-box;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.g-badge-primary {
+ .arco-badge-number {
+ background-color: rgb(var(--primary-5));
+ }
+}
+
+.g-badge-success {
+ .arco-badge-number {
+ background-color: rgb(var(--success-5));
+ }
+}
+
+.g-badge-warning {
+ .arco-badge-number {
+ background-color: rgb(var(--warning-5));
+ }
+}
+
+.g-row-tool {
+ gap: 8px;
+ margin-bottom: 12px;
+}
+
+// 以下这些类名要放到下面,涉及到css权重优先级问题
+.g-mt {
+ margin-top: var(--margin);
+}
+
+.g-mb {
+ margin-bottom: var(--margin);
+}
+
+.g-ml {
+ margin-left: var(--margin);
+}
+
+.g-mr {
+ margin-right: var(--margin);
+}
+
+.g-mx {
+ margin-right: var(--margin);
+ margin-left: var(--margin);
+}
+
+.g-my {
+ margin-top: var(--margin);
+ margin-bottom: var(--margin);
+}
+
+.g-m0 {
+ margin: 0;
+}
+
+.g-pt {
+ padding-top: var(--margin);
+}
+
+.g-pb {
+ padding-bottom: var(--margin);
+}
+
+.g-pl {
+ padding-left: var(--margin);
+}
+
+.g-pr {
+ padding-right: var(--margin);
+}
+
+.g-px {
+ padding-right: var(--padding);
+ padding-left: var(--padding);
+}
+
+.g-py {
+ padding-top: var(--padding);
+ padding-bottom: var(--padding);
+}
+
+.g-p0 {
+ padding: 0;
+}
+
+.g-round {
+ border-radius: 100px;
+}
+
+.w-full {
+ width: 100%;
+}
+
+.h-full {
+ height: 100%;
+}
+
+.overflow-hidden {
+ overflow: hidden;
+}
+
+.flex-1 {
+ flex: 1;
+}
+
+// 一般结合使用
+.flex-column {
+ flex-direction: column;
+ overflow: hidden;
+}
+
+// 通用外边距
+.g-margin {
+ margin: var(--margin);
+}
+
+// 通用内边距
+.g-padding {
+ padding: var(--padding);
+}
+
+.g-select-input {
+ flex-shrink: 0;
+ width: 100px;
+}
+
+.g-search-input {
+ flex-shrink: 0;
+ width: 250px;
+}
+
+.g-line-1 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.g-line-2 {
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+}
+
+.g-line-3 {
+ -webkit-line-clamp: 3;
+ line-clamp: 3;
+}
+
+.g-line-4 {
+ -webkit-line-clamp: 4;
+ line-clamp: 4;
+}
+
+.g-line-5 {
+ -webkit-line-clamp: 5;
+ line-clamp: 5;
+}
+
+.g-line-2,
+.g-line-3,
+.g-line-4,
+.g-line-5 {
+ display: -webkit-box; /* 弹性伸缩盒 */
+ overflow: hidden;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ -webkit-box-orient: vertical; /* 设置伸缩盒子元素排列方式 */
+}
\ No newline at end of file
diff --git a/frontend-pc/src/styles/index.scss b/frontend-pc/src/styles/index.scss
new file mode 100644
index 0000000..8e0471c
--- /dev/null
+++ b/frontend-pc/src/styles/index.scss
@@ -0,0 +1,17 @@
+// 基础样式
+@use './base.scss';
+
+// 全局类名样式
+@use './global.scss';
+
+// 自定义原生滚动条样式
+@use './scrollbar-reset.scss';
+
+// 自定义 nprogress 插件进度条颜色
+@use './nprogress.scss';
+
+// 富文本的css主题颜色变量
+@use './editor.scss';
+
+// 动画类名
+@use './animated.scss';
\ No newline at end of file
diff --git a/frontend-pc/src/styles/mixin.scss b/frontend-pc/src/styles/mixin.scss
new file mode 100644
index 0000000..0485c99
--- /dev/null
+++ b/frontend-pc/src/styles/mixin.scss
@@ -0,0 +1,21 @@
+@mixin scrollbar($size: 6px, $color: #d9d9d9) {
+ &::-webkit-scrollbar-thumb {
+ background-color: $color;
+ border-radius: $size;
+ }
+
+ &::-webkit-scrollbar-thumb:hover {
+ background-color: $color;
+ border-radius: $size;
+ }
+
+ &::-webkit-scrollbar {
+ width: $size;
+ height: $size;
+ }
+
+ &::-webkit-scrollbar-track-piece {
+ background-color: rgb(0 0 0 / 0%);
+ border-radius: 0;
+ }
+}
diff --git a/frontend-pc/src/styles/nprogress.scss b/frontend-pc/src/styles/nprogress.scss
new file mode 100644
index 0000000..000dc90
--- /dev/null
+++ b/frontend-pc/src/styles/nprogress.scss
@@ -0,0 +1,4 @@
+// nprogress插件进度条颜色
+#nprogress .bar {
+ background: rgb(var(--primary-6), 0.5) !important; //自定义颜色
+}
diff --git a/frontend-pc/src/styles/scrollbar-reset.scss b/frontend-pc/src/styles/scrollbar-reset.scss
new file mode 100644
index 0000000..87909f9
--- /dev/null
+++ b/frontend-pc/src/styles/scrollbar-reset.scss
@@ -0,0 +1,22 @@
+/* 美化滚动条 */
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+
+::-webkit-scrollbar-thumb {
+ width: 6px;
+ background-color: var(--color-neutral-3);
+ border-radius: 6px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ width: 6px;
+ background-color: var(--color-neutral-4);
+}
+
+::-ms-scrollbar-thumb {
+ width: 6px;
+ background-color: var(--color-neutral-3);
+ border-radius: 6px;
+}
diff --git a/frontend-pc/src/styles/var.scss b/frontend-pc/src/styles/var.scss
new file mode 100644
index 0000000..5cc6e17
--- /dev/null
+++ b/frontend-pc/src/styles/var.scss
@@ -0,0 +1,3 @@
+// 自定义全局scss变量
+// 建议优先使用arco内置的全局css变量来开发,可以有效提高效率和团队协作
+// $color-theme: rgb(var(--primary-6));
\ No newline at end of file
diff --git a/frontend-pc/src/types/api.d.ts b/frontend-pc/src/types/api.d.ts
new file mode 100644
index 0000000..fc0a469
--- /dev/null
+++ b/frontend-pc/src/types/api.d.ts
@@ -0,0 +1,13 @@
+/** 接口返回数据格式 */
+interface ApiRes {
+ code: number
+ data: T
+ message: string
+ success: boolean
+}
+
+/** 分页返回的数据格式 */
+interface PageRes {
+ records: T
+ total: number
+}
diff --git a/frontend-pc/src/types/app.d.ts b/frontend-pc/src/types/app.d.ts
new file mode 100644
index 0000000..860af56
--- /dev/null
+++ b/frontend-pc/src/types/app.d.ts
@@ -0,0 +1,14 @@
+declare namespace App {
+ /** 系统配置 */
+ interface SettingConfig {
+ themeColor: string // 主题色
+ layout: 'left' | 'mix' | 'top' | 'columns'
+ tabStyle: 'card' | 'card-gutter' | 'rounded' | 'custom1' | 'custom2' // 页签风格
+ isTabVisible: boolean // 是否显示页签
+ transitionName: 'zoom-fade' | 'slide-dynamic-origin' | 'fade-slide' | 'fade' | 'fade-bottom' | 'fade-scale' // 动画类名
+ isTransitionEnabled: boolean // 是否启用页面切换动画
+ isMenuCollapsed: boolean // 左侧菜单折叠状态
+ isMenuAccordion: boolean // 左侧菜单手风琴效果
+ isMenuDark: boolean // 菜单深色模式
+ }
+}
diff --git a/frontend-pc/src/types/env.d.ts b/frontend-pc/src/types/env.d.ts
new file mode 100644
index 0000000..72a6327
--- /dev/null
+++ b/frontend-pc/src/types/env.d.ts
@@ -0,0 +1,12 @@
+///
+
+/** 声明环境变量的类型 */
+interface ImportMetaEnv {
+ readonly VITE_API_PREFIX: string
+ readonly VITE_API_BASE_URL: string
+ readonly VITE_BASE: string
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/frontend-pc/src/types/global.d.ts b/frontend-pc/src/types/global.d.ts
new file mode 100644
index 0000000..99432f0
--- /dev/null
+++ b/frontend-pc/src/types/global.d.ts
@@ -0,0 +1,23 @@
+interface AnyObject {
+ [key: string]: unknown
+}
+
+interface Options {
+ value: unknown
+ label: string
+}
+
+interface NodeOptions extends Options {
+ children?: NodeOptions[]
+}
+
+type TimeRanger = [string, string]
+
+/** 系统管理通用状态 与字典 STATUS 一致,'0' 禁用 '1' 启用 */
+type Status = '0' | '1'
+
+/** 性别 与字典 GENDER 一致,'1' 男 '2' 女 '3' 保密 */
+type Gender = '1' | '2' | '3'
+
+/** 接口需要的分页参数 */
+type Pagination = { page: number, size: number }
diff --git a/frontend-pc/src/types/module.d.ts b/frontend-pc/src/types/module.d.ts
new file mode 100644
index 0000000..07078c0
--- /dev/null
+++ b/frontend-pc/src/types/module.d.ts
@@ -0,0 +1,3 @@
+declare module 'nprogress'
+declare module '@arco-design/color'
+declare module 'qs'
diff --git a/frontend-pc/src/types/router.d.ts b/frontend-pc/src/types/router.d.ts
new file mode 100644
index 0000000..4423b27
--- /dev/null
+++ b/frontend-pc/src/types/router.d.ts
@@ -0,0 +1,43 @@
+import 'vue-router'
+
+declare module 'vue-router' {
+ interface RouteMeta {
+ /** 设置该路由在侧边栏和面包屑中展示的名字 */
+ title?: string
+ /** 设置该路由的图标 */
+ icon?: string
+ /** 默认false, 设置true的时候该路由不会在侧边栏出现 */
+ hidden?: boolean
+ /** 默认true, 如果设置为false, 则不会在面包屑中显示 */
+ breadcrumb?: boolean
+ /** 默认true, 如果设置为false, 它则不会显示在Tab栏中 */
+ showInTabs?: boolean
+ /** 默认false, 如果设置为true, 它则会固定在Tab栏中, 例如首页 */
+ affix?: boolean
+ /**
+ * 当一个路由下面的children声明的路由大于1个时, 自动会变成嵌套的模式
+ * 只有一个时, 会将那个子路由当做根路由显示在侧边栏
+ * 若想不管路由下面的children声明的个数都显示你的根路由
+ * 可以设置alwaysShow: true, 这样就会忽略之前定义的规则, 一直显示根路由
+ */
+ alwaysShow?: boolean
+ /**
+ * 示例: activeMenu: "/xxx/xxx"
+ * 当设置了该属性进入路由时, 则会高亮activeMenu属性对应的侧边栏
+ * 该属性适合使用在有hidden: true属性的路由上
+ */
+ activeMenu?: string
+ /**
+ * 是否缓存该路由页面
+ * 默认为false, 为true时代表需要缓存, 此时该路由和该页面都需要设置一致的name
+ */
+ keepAlive?: boolean
+ /** 默认路由参数 */
+ query?: string
+ noShowingChildren?: boolean
+ /** 设置该路由进入的权限, 支持多个权限叠加 */
+ roles?: string[]
+ /** 排序 */
+ sort?: number
+ }
+}
diff --git a/frontend-pc/src/types/shims-vue.d.ts b/frontend-pc/src/types/shims-vue.d.ts
new file mode 100644
index 0000000..aafef95
--- /dev/null
+++ b/frontend-pc/src/types/shims-vue.d.ts
@@ -0,0 +1,8 @@
+///
+
+declare module '*.vue' {
+ import type { DefineComponent } from 'vue'
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+ const component: DefineComponent<{}, {}, any>
+ export default component
+}
diff --git a/frontend-pc/src/types/tool.d.ts b/frontend-pc/src/types/tool.d.ts
new file mode 100644
index 0000000..0383038
--- /dev/null
+++ b/frontend-pc/src/types/tool.d.ts
@@ -0,0 +1,14 @@
+// 辅助类型:获取所有类型的键的联合
+type AllKeys = T extends any ? keyof T : never
+
+// 辅助类型:获取属性在所有类型中的联合类型
+type UnionType = T extends any
+ ? K extends keyof T
+ ? T[K]
+ : never
+ : never
+
+// 合并多个类型,同名属性转为联合类型
+export type MergeMultiple = {
+ [K in AllKeys]?: UnionType
+}
diff --git a/frontend-pc/src/utils/auth.ts b/frontend-pc/src/utils/auth.ts
new file mode 100644
index 0000000..6dee588
--- /dev/null
+++ b/frontend-pc/src/utils/auth.ts
@@ -0,0 +1,39 @@
+/** Token 存储键名 */
+const TOKEN_KEY = 'token'
+
+/**
+ * 检查用户是否已登录
+ * @description 通过检查 localStorage 中是否存在 token 来判断用户登录状态
+ * @returns {boolean} 是否已登录
+ */
+const isLogin = () => {
+ return !!localStorage.getItem(TOKEN_KEY)
+}
+
+/**
+ * 获取用户 Token
+ * @description 从 localStorage 中获取存储的 token
+ * @returns {string | null} 用户 token,未登录时返回 null
+ */
+const getToken = () => {
+ return localStorage.getItem(TOKEN_KEY)
+}
+
+/**
+ * 设置用户 Token
+ * @description 将用户 token 存储到 localStorage 中
+ * @param token - 用户 token
+ */
+const setToken = (token: string) => {
+ localStorage.setItem(TOKEN_KEY, token)
+}
+
+/**
+ * 清除用户 Token
+ * @description 从 localStorage 中移除用户 token
+ */
+const clearToken = () => {
+ localStorage.removeItem(TOKEN_KEY)
+}
+
+export { clearToken, getToken, isLogin, setToken }
diff --git a/frontend-pc/src/utils/createSelectDialog.ts b/frontend-pc/src/utils/createSelectDialog.ts
new file mode 100644
index 0000000..9e5cece
--- /dev/null
+++ b/frontend-pc/src/utils/createSelectDialog.ts
@@ -0,0 +1,103 @@
+import type { Component } from 'vue'
+import { Message, Modal } from '@arco-design/web-vue'
+import { h, ref } from 'vue'
+
+interface CreateSelectDialogParams {
+ title: string
+ component: Component
+ componentProps?: Record
+ tip?: string
+}
+
+/**
+ * 选择弹窗配置选项接口
+ * @template T 选中数据的类型
+ */
+interface SelectDialogOptions {
+ /** 弹窗标题(会覆盖创建参数中的title) */
+ title?: string
+ /** 是否允许多选 */
+ multiple?: boolean
+ /** 查询参数,通常用于初始化数据 */
+ queryParams?: Record
+ /** 传递给组件的额外属性 */
+ componentProps?: Record
+ /** 点击确定按钮后的回调函数 */
+ onOk?: (data: T) => void
+ /** 点击确定前的校验函数,返回Promise决定是否允许确定 */
+ onBeforeOk?: (data: T) => Promise
+}
+
+/**
+ * 创建一个选择类型的弹窗工厂函数
+ *
+ * 该函数返回一个创建特定类型选择弹窗的方法,适用于需要从列表中选择数据的场景。
+ * 内部使用Vue的createVNode动态渲染组件,并通过ref获取组件实例的方法。
+ *
+ * @template T 选中数据的类型
+ * @param {CreateSelectDialogParams} params 创建弹窗所需的基本参数
+ * @returns {(options: SelectDialogOptions) => void} 可配置的弹窗创建函数
+ *
+ * @example
+ * // 创建一个用户选择弹窗
+ * const selectUserDialog = createSelectDialog({
+ * title: '选择用户',
+ * component: UserSelectComponent,
+ * tip: '请至少选择一个用户'
+ * })
+ *
+ * // 打开弹窗并处理选择结果
+ * selectUserDialog({
+ * multiple: true,
+ * queryParams: { status: 'active' },
+ * onOk: (selectedUsers) => {
+ * console.log('已选择用户:', selectedUsers)
+ * }
+ * })
+ */
+export const createSelectDialog = (params: CreateSelectDialogParams) => {
+ return (options: SelectDialogOptions) => {
+ const TableRef = ref()
+ Modal.open({
+ // 优先使用options中的title,否则使用params中的title
+ title: options.title || params.title,
+ // 动态渲染传入的组件,设置ref引用并合并属性
+ content: () => h(params.component, {
+ ref: (e: any) => (TableRef.value = e),
+ multiple: options.multiple,
+ queryParams: options.queryParams,
+ ...params.componentProps,
+ ...options.componentProps
+ }),
+ // 设置弹窗宽度自适应
+ width: 'calc(100% - 20px)',
+ modalStyle: { maxWidth: '1000px' },
+ bodyStyle: { overflow: 'hidden', height: '500px', padding: 0 },
+ onBeforeOk: async () => {
+ // 检查组件是否暴露了必要的getSelectedData方法
+ if (!TableRef.value?.getSelectedData) {
+ Message.warning('组件必须暴露getSelectedData方法')
+ return false
+ }
+
+ // 获取选中的数据
+ const data = TableRef.value?.getSelectedData?.() || []
+
+ // 验证是否选择了数据
+ if (!data.length) {
+ Message.warning(params.tip || '请选择数据')
+ return false
+ }
+
+ // 如果提供了前置校验函数,则调用并根据结果决定是否继续
+ if (options?.onBeforeOk) {
+ return await options.onBeforeOk(data)
+ }
+
+ // 调用确定回调函数,传递选中的数据
+ options.onOk?.(data)
+ return true
+ }
+ })
+ }
+}
diff --git a/frontend-pc/src/utils/downloadFile.ts b/frontend-pc/src/utils/downloadFile.ts
new file mode 100644
index 0000000..bc3baa7
--- /dev/null
+++ b/frontend-pc/src/utils/downloadFile.ts
@@ -0,0 +1,76 @@
+/**
+ * 根据文件url获取文件名
+ * @param url 文件url
+ */
+function getFileName(url: string) {
+ const num = url.lastIndexOf('/') + 1
+ let fileName = url.substring(num)
+ // 把参数和文件名分割开
+ fileName = decodeURI(fileName.split('?')[0])
+ return fileName
+}
+
+/**
+ * 根据文件地址下载文件
+ * @param {*} sUrl
+ */
+export function downloadByUrl({
+ url,
+ target = '_blank',
+ fileName
+}: {
+ url: string
+ target?: '_self' | '_blank'
+ fileName?: string
+}): Promise {
+ // 是否同源
+ const isSameHost = new URL(url).host === location.host
+ return new Promise((resolve, reject) => {
+ if (isSameHost) {
+ const link = document.createElement('a')
+ link.href = url
+ link.target = target
+
+ if (link.download !== undefined) {
+ link.download = fileName || getFileName(url)
+ }
+
+ if (document.createEvent) {
+ const e = document.createEvent('MouseEvents')
+ e.initEvent('click', true, true)
+ link.dispatchEvent(e)
+ return resolve(true)
+ }
+
+ if (!url.includes('?')) {
+ url += '?download'
+ }
+
+ window.open(url, target)
+ return resolve(true)
+ } else {
+ const canvas = document.createElement('canvas')
+ const img = document.createElement('img')
+ img.setAttribute('crossOrigin', 'Anonymous')
+ img.src = url
+ img.onload = () => {
+ canvas.width = img.width
+ canvas.height = img.height
+ const context = canvas.getContext('2d')!
+ context.drawImage(img, 0, 0, img.width, img.height)
+ // window.navigator.msSaveBlob(canvas.msToBlob(),'image.jpg');
+ // saveAs(imageDataUrl, '附件');
+ canvas.toBlob((blob) => {
+ const link = document.createElement('a')
+ if (!blob) return
+ link.href = window.URL.createObjectURL(blob)
+ link.download = getFileName(url)
+ link.click()
+ URL.revokeObjectURL(link.href)
+ resolve(true)
+ }, 'image/jpeg')
+ }
+ img.onerror = (e) => reject(e)
+ }
+ })
+}
diff --git a/frontend-pc/src/utils/file.ts b/frontend-pc/src/utils/file.ts
new file mode 100644
index 0000000..8848413
--- /dev/null
+++ b/frontend-pc/src/utils/file.ts
@@ -0,0 +1,71 @@
+/** 文件类型列表项接口 */
+export interface FileTypeListItem {
+ /** 显示名称 */
+ name: string
+ /** 类型值 */
+ value: string
+ /** 菜单图标 */
+ menuIcon: string
+ /** 文件图标 */
+ icon: string
+}
+
+/**
+ * 文件类型列表
+ * @description 定义系统中支持的文件类型分类
+ */
+export const FILE_TYPE_LIST: FileTypeListItem[] = [
+ { name: '全部', value: '0', menuIcon: 'file-all', icon: 'icon-stamp' },
+ { name: '图片', value: '1', menuIcon: 'file-image', icon: 'icon-file-image' },
+ { name: '文档', value: '2', menuIcon: 'file-txt', icon: 'icon-file' },
+ { name: '视频', value: '3', menuIcon: 'file-video', icon: 'icon-video-camera' },
+ { name: '音频', value: '4', menuIcon: 'file-music', icon: 'icon-file-audio' },
+ { name: '其他', value: '5', menuIcon: 'file-other', icon: 'icon-bulb' }
+]
+
+/** 文件扩展名与图标映射接口 */
+export interface FileExtendNameIconMap {
+ [key: string]: string
+}
+
+/**
+ * 文件类型图标映射
+ * @description 定义文件扩展名与对应图标的映射关系
+ */
+export const FILE_ICON_MAP: FileExtendNameIconMap = {
+ // 音频文件
+ mp3: 'file-music',
+ // 视频文件
+ mp4: 'file-video',
+ // 文件夹
+ dir: 'file-dir',
+ // Office 文件
+ ppt: 'file-ppt',
+ doc: 'file-wps',
+ docx: 'file-wps',
+ xls: 'file-excel',
+ xlsx: 'file-excel',
+ // 文本文件
+ txt: 'file-txt',
+ // 压缩文件
+ rar: 'file-rar',
+ zip: 'file-zip',
+ // 网页文件
+ html: 'file-html',
+ css: 'file-css',
+ js: 'file-js',
+ // 未知文件
+ other: 'file-other'
+}
+
+/**
+ * 图片文件类型列表
+ * @description 系统支持的图片文件扩展名
+ */
+export const IMAGE_TYPES = ['jpg', 'png', 'gif', 'jpeg']
+
+/**
+ * Office 文件类型列表
+ * @description 系统支持的 Office 文件扩展名
+ */
+export const OFFICE_TYPES = ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx']
diff --git a/frontend-pc/src/utils/http.ts b/frontend-pc/src/utils/http.ts
new file mode 100644
index 0000000..79ee043
--- /dev/null
+++ b/frontend-pc/src/utils/http.ts
@@ -0,0 +1,216 @@
+import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
+import { Message, Notification } from '@arco-design/web-vue'
+import axios from 'axios'
+import NProgress from 'nprogress'
+import router from '@/router'
+import { getToken } from '@/utils/auth'
+import 'nprogress/nprogress.css'
+
+// 配置 NProgress
+NProgress.configure({ showSpinner: false })
+
+/**
+ * 本项目后端响应契约(《03-接口设计》§1.3):
+ *
+ * { code: 0 | 4xxxx | 5xxxx, msg: string, data: T }
+ *
+ * 与模板内部的 ApiRes 契约不同,差别有三处,都在下面这层适配里统一处理:
+ *
+ * | 维度 | 本项目后端 | 模板内部约定 |
+ * |----------|--------------------|--------------------|
+ * | 成功判定 | code === 0 | success: boolean |
+ * | 提示文案 | msg | message |
+ * | 分页列表 | { list, total } | { records, total } |
+ *
+ * ⚠️ 适配放在这里而不是逐个改 api 文件:这三条是全后端统一的契约,
+ * 分散到每个 listAPI 去转换,漏一个就是一个"列表永远空着"的 bug。
+ */
+
+/** 本项目后端的成功码 */
+const CODE_SUCCESS = 0
+/** 未登录 / token 失效 */
+const CODE_UNAUTHORIZED = 40101
+/** 无权限(含跨租户) */
+const CODE_FORBIDDEN = 40301
+/** 限流 */
+const CODE_RATE_LIMITED = 42901
+
+/** 传输层 HTTP 状态码文案(业务错误不走这里,见下方响应拦截器) */
+const StatusCodeMessage: Record = {
+ 400: '请求错误(400)',
+ 401: '未授权,请重新登录(401)',
+ 403: '拒绝访问(403)',
+ 404: '请求出错(404)',
+ 408: '请求超时(408)',
+ 500: '服务器错误(500)',
+ 501: '服务未实现(501)',
+ 502: '网络错误(502)',
+ 503: '服务不可用(503)',
+ 504: '网络超时(504)'
+}
+
+/** 创建 axios 实例 */
+const http: AxiosInstance = axios.create({
+ baseURL: import.meta.env.VITE_API_PREFIX,
+ timeout: 30 * 1000
+})
+
+/**
+ * 把分页字段补一份 records。
+ * 后端返回 { list, total },模板的 useTable / PageRes 读 records。
+ * 补而不替:list 原样保留,两边都能取到,避免破坏原有调用。
+ */
+function normalizePageShape(payload: unknown): unknown {
+ if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
+ const obj = payload as Record
+ if (Array.isArray(obj.list) && typeof obj.total === 'number' && obj.records === undefined) {
+ return { ...obj, records: obj.list }
+ }
+ }
+ return payload
+}
+
+/** 跳登录页(只跳一次,避免一个页面多个请求同时失效时反复跳) */
+let redirecting = false
+function toLogin() {
+ if (redirecting) return
+ redirecting = true
+ const back = encodeURIComponent(router.currentRoute.value.fullPath)
+ router.replace(`/login?redirect=${back}`).finally(() => {
+ setTimeout(() => {
+ redirecting = false
+ }, 800)
+ })
+}
+
+// 请求拦截器
+http.interceptors.request.use(
+ (config: AxiosRequestConfig) => {
+ NProgress.start()
+ const token = getToken()
+ if (token) {
+ if (!config.headers) {
+ config.headers = {}
+ }
+ // 本项目后端用标准的 Authorization: Bearer(《03-接口设计》§1.2),
+ // 不是模板默认的 token 自定义头
+ config.headers.Authorization = `Bearer ${token}`
+ }
+ return config
+ },
+ (error) => {
+ NProgress.done()
+ return Promise.reject(error)
+ }
+)
+
+// 响应拦截器
+http.interceptors.response.use(
+ (response: AxiosResponse) => {
+ NProgress.done()
+ const raw = response.data
+
+ // 非信封响应(文件流等)直接放行
+ if (!raw || typeof raw.code !== 'number') {
+ return response
+ }
+
+ // 未登录:清 token 并跳登录
+ if (raw.code === CODE_UNAUTHORIZED) {
+ toLogin()
+ return Promise.reject(new Error(raw.msg || '登录已失效,请重新登录'))
+ }
+
+ // 业务成功:适配成模板的 ApiRes 形状
+ if (raw.code === CODE_SUCCESS) {
+ response.data = {
+ code: 200,
+ success: true,
+ message: raw.msg || 'success',
+ data: normalizePageShape(raw.data)
+ }
+ return response
+ }
+
+ // 业务失败:按文案长度选择 Message(短)或 Notification(长),与原实现一致
+ const message = raw.msg || '服务器端错误'
+ if (message.length <= 15) {
+ Message.error(message)
+ } else {
+ Notification.error(message)
+ }
+ // 把后端业务码附在错误上,便于调用方按码分支(如租户重名)
+ const err = new Error(message) as Error & { bizCode?: number, bizData?: unknown }
+ err.bizCode = raw.code
+ err.bizData = raw.data
+ return Promise.reject(err)
+ },
+ (error) => {
+ NProgress.done()
+ Message.clear()
+ const response = Object.assign({}, error.response)
+
+ // 401/403/429 这三个后端用真实 HTTP 状态码(《03-接口设计》§1.3)
+ if (response?.status === 401) {
+ toLogin()
+ } else {
+ const msg = StatusCodeMessage[response?.status] || '系统异常, 请检查网络或联系管理员!'
+ Message.error(msg)
+ }
+
+ const err = new Error(error.message) as Error & { status?: number }
+ err.status = response?.status
+ return Promise.reject(err)
+ }
+)
+
+/** 封装请求方法 */
+const request = (config: AxiosRequestConfig): Promise> => {
+ return new Promise((resolve, reject) => {
+ http
+ .request(config)
+ .then((res: AxiosResponse) => resolve(res.data))
+ .catch((err: { message: string }) => reject(err))
+ })
+}
+
+/** GET 请求 */
+const get = (url: string, params?: object, config?: AxiosRequestConfig): Promise> => {
+ return request({
+ method: 'get',
+ url,
+ params,
+ ...config
+ })
+}
+
+/** POST 请求 */
+const post = (url: string, params?: object, config?: AxiosRequestConfig): Promise> => {
+ return request({
+ method: 'post',
+ url,
+ data: params,
+ ...config
+ })
+}
+
+/** PUT 请求(本项目后端大量使用 PUT,模板原来只有 get/post) */
+const put = (url: string, params?: object, config?: AxiosRequestConfig): Promise> => {
+ return request({
+ method: 'put',
+ url,
+ data: params,
+ ...config
+ })
+}
+
+/** DELETE 请求 */
+const del = (url: string, config?: AxiosRequestConfig): Promise> => {
+ return request({
+ method: 'delete',
+ url,
+ ...config
+ })
+}
+
+export default { get, post, put, delete: del, request }
diff --git a/frontend-pc/src/utils/index.ts b/frontend-pc/src/utils/index.ts
new file mode 100644
index 0000000..7383c54
--- /dev/null
+++ b/frontend-pc/src/utils/index.ts
@@ -0,0 +1,297 @@
+import { camelCase, upperFirst } from 'lodash-es'
+import { browse, mapTree } from 'xe-utils'
+import { isExternal } from '@/utils/validate'
+
+export * from './modules/getFieldToRangTimeForm'
+export * from './modules/getRangeTimeToFieldForm'
+
+/**
+ * @description 工具函数库
+ * @recommend 优先使用 lodash-es、xe-utils、dayjs 等成熟库的方法
+ */
+
+// =============== 类型定义 ===============
+
+/** 字符串去除空格的位置 */
+type TrimPosition = 'both' | 'left' | 'right' | 'all'
+
+/** 树形结构基础接口 */
+interface TreeNode {
+ children?: TreeNode[]
+ [key: string]: any
+}
+
+/** 可排序的树节点 */
+interface SortableTreeNode extends TreeNode {
+ sort: number
+ children?: SortableTreeNode[]
+}
+
+// =============== 对象操作 ===============
+
+/**
+ * 获取对象属性值(类型安全)
+ * @param obj - 目标对象
+ * @param key - 属性键名
+ * @returns 属性值
+ */
+export function getProperty(obj: T, key: K): T[K] {
+ return obj[key]
+}
+
+/**
+ * 深拷贝
+ * @param data - 要拷贝的数据
+ * @returns 深拷贝后的数据
+ */
+export function deepClone(data: T): T {
+ if (typeof data !== 'object' || data === null) return data
+ const newData: any = Array.isArray(data) ? [] : {}
+ for (const key in data) {
+ newData[key] = typeof data[key] === 'object' ? deepClone(data[key]) : data[key]
+ }
+ return newData
+}
+
+// =============== 字符串操作 ===============
+
+/**
+ * 去除字符串空格
+ * @param str - 目标字符串
+ * @param pos - 去除位置,默认两端
+ * @returns 处理后的字符串
+ */
+export function trim(str: string, pos: TrimPosition = 'both'): string {
+ const trimMap = {
+ both: /^\s+|\s+$/g,
+ left: /^\s*/,
+ right: /(\s*$)/g,
+ all: /\s+/g
+ }
+ return str.replace(trimMap[pos] || trimMap.both, '')
+}
+
+/**
+ * 字符串大小写转换
+ * @param str - 目标字符串
+ * @param type - 转换类型:1-全大写 2-全小写 3-首字母大写
+ * @returns 转换后的字符串
+ */
+export function toCase(str: string, type: 1 | 2 | 3): string {
+ const caseMap = {
+ 1: () => str.toUpperCase(),
+ 2: () => str.toLowerCase(),
+ 3: () => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()
+ }
+ return caseMap[type]?.() || str
+}
+
+/**
+ * 格式化电话号码
+ * @param mobile - 手机号
+ * @param separator - 分隔符
+ * @returns 格式化后的电话号码,如:183-7983-6654
+ */
+export function formatPhone(mobile: string, separator = '-'): string {
+ return mobile.replace(/(?=(\d{4})+$)/g, separator)
+}
+
+/**
+ * 手机号脱敏处理
+ * @param phone - 手机号
+ * @returns 脱敏后的手机号,如:155****8810
+ */
+export function hidePhone(phone: string): string {
+ return phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
+}
+
+// =============== 数字操作 ===============
+
+/**
+ * 获取指定范围内的随机整数
+ * @param min - 最小值
+ * @param max - 最大值
+ * @returns 随机整数
+ */
+export function getRandomInteger(min = 0, max: number): number {
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+
+/**
+ * 数组计算工具
+ */
+export const arrayMath = {
+ /** 获取数组最大值 */
+ max: (arr: number[]): number => Math.max(...arr),
+ /** 获取数组最小值 */
+ min: (arr: number[]): number => Math.min(...arr),
+ /** 计算数组总和 */
+ sum: (arr: number[]): number => arr.reduce((a, b) => a + b, 0),
+ /** 计算数组平均值 */
+ average: (arr: number[]): number => arrayMath.sum(arr) / arr.length
+}
+
+// =============== 数据验证 ===============
+
+/**
+ * 检测数据是否为空
+ * @param data - 要检测的数据
+ * @returns 是否为空
+ */
+export function isEmpty(data: unknown): boolean {
+ if (data === '' || data == null || data === 'undefined' || data === 'null') return true
+ if (typeof data === 'object') {
+ return JSON.stringify(data) === '{}'
+ || JSON.stringify(data) === '[]'
+ || JSON.stringify(data) === '[{}]'
+ }
+ return false
+}
+
+/**
+ * 获取数据类型
+ * @param value - 要检测的值
+ * @returns 数据类型字符串
+ */
+export function getTypeOf(value: unknown): string {
+ return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
+}
+
+// =============== 日期时间 ===============
+
+/**
+ * 判断是否为闰年
+ * @param year - 年份
+ * @returns 是否闰年
+ */
+export function isLeapYear(year: number): boolean {
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
+}
+
+/**
+ * 获取问候语
+ * @returns 根据当前时间返回问候语
+ */
+export function goodTimeText(): string {
+ const hour = new Date().getHours()
+ if (hour < 9) return '早上好'
+ if (hour <= 11) return '上午好'
+ if (hour <= 13) return '中午好'
+ if (hour < 20) return '下午好'
+ return '晚上好'
+}
+
+// =============== 文件处理 ===============
+
+/**
+ * 格式化文件大小
+ * @param size - 文件大小(字节)
+ * @returns 格式化后的文件大小
+ */
+export function formatFileSize(size: number): string {
+ const units = ['B', 'KB', 'MB', 'GB']
+ let index = 0
+ while (size > 1024 && index < units.length - 1) {
+ size /= 1024
+ index++
+ }
+ return `${Math.round(size * 100) / 100}${units[index]}`
+}
+
+// =============== 颜色处理 ===============
+
+/**
+ * RGB转十六进制
+ * @param r - 红色值(0-255)
+ * @param g - 绿色值(0-255)
+ * @param b - 蓝色值(0-255)
+ * @returns 十六进制颜色值
+ */
+export function rgbToHex(r: number, g: number, b: number): string {
+ return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`
+}
+
+/**
+ * 十六进制颜色转 RGB
+ * @param {string} hex - 十六进制颜色,如 #377DFF、377DFF、#FFF
+ * @returns {string} rgb(r, g, b) 格式字符串
+ */
+export function hexToRgb(hex: string): string {
+ hex = hex.replace(/^#/, '')
+ if (hex.length === 3) {
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
+ }
+ const r = Number.parseInt(hex.substring(0, 2), 16)
+ const g = Number.parseInt(hex.substring(2, 4), 16)
+ const b = Number.parseInt(hex.substring(4, 6), 16)
+
+ return `${r}, ${g}, ${b}`
+}
+
+/**
+ * 获取随机十六进制颜色
+ * @returns 随机颜色值
+ */
+export function randomHex(): string {
+ return `#${Math.floor(Math.random() * 0xFFFFFF).toString(16).padEnd(6, '0')}`
+}
+
+// =============== 路由工具 ===============
+
+/**
+ * 转换路由路径为名称
+ * @param path - 路由路径
+ * @returns 转换后的名称
+ * @example
+ * /system => System
+ * /system/menu => SystemMenu
+ * /crud/detail => CrudDetail
+ */
+export function transformPathToName(path: string): string {
+ if (!path || isExternal(path)) return ''
+ return upperFirst(camelCase(path))
+}
+
+// =============== 树形结构处理 ===============
+
+/**
+ * 过滤树形结构
+ * @param array - 树形数组
+ * @param predicate - 过滤函数
+ * @returns 过滤后的树形数组
+ */
+export function filterTree(array: TreeNode[], predicate: (node: TreeNode) => boolean): TreeNode[] {
+ const filtered = array.filter(predicate)
+ return mapTree(filtered, (item) => {
+ if (item.children?.length) {
+ item.children = item.children.filter(predicate)
+ }
+ return item
+ })
+}
+
+/**
+ * 排序树形结构
+ * @param array - 可排序的树形数组
+ * @returns 排序后的树形数组
+ */
+export function sortTree(array: SortableTreeNode[]): SortableTreeNode[] {
+ const sort = (a: SortableTreeNode, b: SortableTreeNode) => (a.sort ?? 0) - (b.sort ?? 0)
+ array.sort(sort)
+ return mapTree(array, (item) => {
+ if (item.children?.length) {
+ item.children.sort(sort)
+ }
+ return item
+ })
+}
+
+// =============== 环境检测 ===============
+
+/**
+ * 检测是否为移动端环境
+ * @returns 是否为移动端
+ */
+export function isMobile(): boolean {
+ return browse().isMobile
+}
diff --git a/frontend-pc/src/utils/mitt.ts b/frontend-pc/src/utils/mitt.ts
new file mode 100644
index 0000000..2b0d8d7
--- /dev/null
+++ b/frontend-pc/src/utils/mitt.ts
@@ -0,0 +1,10 @@
+import mitt from 'mitt'
+
+type Events = {
+ // 自定义事件名称
+ changeText: string
+}
+
+const mittBus = mitt()
+
+export default mittBus
diff --git a/frontend-pc/src/utils/modules/getFieldToRangTimeForm.ts b/frontend-pc/src/utils/modules/getFieldToRangTimeForm.ts
new file mode 100644
index 0000000..cc9cc40
--- /dev/null
+++ b/frontend-pc/src/utils/modules/getFieldToRangTimeForm.ts
@@ -0,0 +1,46 @@
+/** F: 表单数据 T: 表单数据排除的属性 V: 表单数据里-时间范围属性拆分成的新属性 */
+type FormatItem = {
+ field: keyof T | keyof F
+ start: keyof V
+ end: keyof V
+}
+
+/**
+ * 合并有时间范围的表单
+ * @param form 表单数据
+ * @param formatList 需要合并的时间范围属性
+ * @returns 合并后的表单数据
+ * @example
+ * const form = {
+ name: '李四',
+ startTime1: '2024-01-01',
+ endTime1: '2024-01-30',
+ startTime2: '2025-01-01',
+ endTime2: '2025-01-30'
+ }
+ *
+ * const data = getFieldToRangTimeForm(form, [
+ { field: 'rangTime1', start: 'startTime1', end: 'endTime1' },
+ { field: 'rangTime2', start: 'startTime2', end: 'endTime2' }
+ ])
+ *
+ * // data的结果为:
+ * // {
+ // name: '李四',
+ // rangTime1: ['2024-01-01', '2024-01-30'],
+ // rangTime2: ['2025-01-01', '2025-01-30']
+ // }
+ */
+export const getFieldToRangTimeForm = (form: F, formatList: FormatItem[]) => {
+ const obj = { ...form } as Omit & T
+ for (const i of formatList) {
+ const startTime = obj[i.start as never] as string
+ const endTime = obj[i.end as never] as string
+ if (startTime && endTime) {
+ (obj[i.field as never] as [string, string]) = [startTime, endTime]
+ }
+ delete obj[i.start as never]
+ delete obj[i.end as never]
+ }
+ return obj
+}
diff --git a/frontend-pc/src/utils/modules/getRangeTimeToFieldForm.ts b/frontend-pc/src/utils/modules/getRangeTimeToFieldForm.ts
new file mode 100644
index 0000000..7a3d29e
--- /dev/null
+++ b/frontend-pc/src/utils/modules/getRangeTimeToFieldForm.ts
@@ -0,0 +1,45 @@
+/** F: 表单数据 T: 表单数据排除的属性 V: 表单数据里-时间范围属性拆分成的新属性 */
+type FormatItem = {
+ field: keyof T | keyof F
+ start: keyof V
+ end: keyof V
+}
+
+/**
+ * 拆分有时间范围的表单
+ * @param form 表单数据
+ * @param formatList 需要拆分的时间范围属性
+ * @returns 时间范围转成指定字段的表单
+ * @example
+ * const form = {
+ name: '张三',
+ rangTime1: ['2024-01-01', '2024-01-30'] as [string, string],
+ rangTime2: ['2025-01-01', '2025-01-30'] as [string, string]
+ }
+ *
+ * const data = getRangeTimeToFieldForm(form, [
+ { field: 'rangTime1', start: 'startTime1', end: 'endTime1' },
+ { field: 'rangTime2', start: 'startTime2', end: 'endTime2' }
+ ])
+ *
+ * // data的结果为:
+ // {
+ // name: '张三',
+ // startTime1: '2024-01-01',
+ // endTime1: '2024-01-30',
+ // startTime2: '2025-01-01',
+ // endTime2: '2025-01-30'
+ // }
+ */
+export const getRangeTimeToFieldForm = (form: F, formatList: FormatItem[]) => {
+ const obj = { ...form } as Omit