diff --git a/.env.development b/.env.development index 9cf069a..03e2369 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,6 @@ -# 开发环境 -VITE_PORT = 5173 -VITE_API_BASE_URL = http://localhost:3000 -VITE_API_PREFIX = /api +# 开发环境配置 +# 基础API地址 +VITE_API_BASE_URL=http://cpms.op.nps.yunvip123.cn/cpms + +# 是否开启本地调试 +LOCAL_DEBUG=true diff --git a/.env.production b/.env.production index dc54b4f..b80cb74 100644 --- a/.env.production +++ b/.env.production @@ -1,3 +1,5 @@ # 生产环境 -VITE_API_BASE_URL = https://api.cpms.example.com -VITE_API_PREFIX = /api +VITE_API_BASE_URL = https://cpmsapi.yunvip123.com + +# 是否开启本地调试 +LOCAL_DEBUG=false diff --git a/.env.test b/.env.test index b3621a6..889ad12 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,6 @@ -# 测试环境 -VITE_API_BASE_URL = https://test-api.cpms.example.com -VITE_API_PREFIX = /api +# 测试环境配置 +# 基础API地址 +VITE_API_BASE_URL=http://cpms.op.nps.yunvip123.cn/cpms + +# 是否开启本地调试 +LOCAL_DEBUG=false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 715094c..2b3c593 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Logs -logs +/logs *.log npm-debug.log* yarn-debug.log* @@ -40,3 +40,7 @@ coverage .cache .temp .tmp + +# Easy LESS 误生成的编译产物(本项目为 Vue PC 端,样式源文件为 .less,由 Vite 编译) +*.wxss +*.module.css diff --git a/index.html b/index.html index d3cfca5..7267ac8 100644 --- a/index.html +++ b/index.html @@ -1,14 +1,17 @@ - - - - - - 六个羽友赛事运营平台 - - -
- - - + + + + + + + 六羽赛事运营平台 + + + +
+ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d6e5eff..796765a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,18 @@ "version": "0.0.0", "dependencies": { "@ant-design/icons-vue": "^7.0.1", + "@types/big.js": "^7.0.0", + "@yp-component/root": "^0.0.48", "ant-design-vue": "^4.0.0", + "big.js": "^7.0.1", + "china-area-data": "^5.0.1", + "dayjs": "^1.11.21", + "echarts": "^5.6.0", "express": "^5.2.1", "html2canvas": "^1.4.1", "qs": "^6.15.0", "vue": "^3.3.0", + "vue-icons-plus": "^0.1.8", "vue-router": "^4.2.0" }, "devDependencies": { @@ -1045,6 +1052,12 @@ "url": "https://ko-fi.com/dangreen" } }, + "node_modules/@types/big.js": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/@types/big.js/-/big.js-7.0.0.tgz", + "integrity": "sha512-WfAGp7IbJvyB8EmWK4tJD24rJRAL6uVbw3LV/hJntFNam+os9KWKj0PzXo8rRRpjupYK8U0M8FoBB8dBhWF2dg==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "dev": true, @@ -1488,6 +1501,15 @@ "version": "3.5.39", "license": "MIT" }, + "node_modules/@yp-component/root": { + "version": "0.0.48", + "resolved": "http://192.168.2.127:4873/@yp-component/root/-/root-0.0.48.tgz", + "integrity": "sha512-TOp8adAob3pMSfecQjEC4uVNCGqNWcShDjSQysd4pYY34i5ldFLRGSiJdpLsFG1xKkcSudiqEc/Si/ZfYoDSqA==", + "dependencies": { + "cropperjs": "^1.6.2", + "vue": "^3.5.25" + } + }, "node_modules/accepts": { "version": "2.0.0", "license": "MIT", @@ -1663,6 +1685,19 @@ "node": ">=6.0.0" } }, + "node_modules/big.js": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-7.0.1.tgz", + "integrity": "sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, "node_modules/body-parser": { "version": "2.3.0", "license": "MIT", @@ -1826,6 +1861,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/china-area-data": { + "version": "5.0.1", + "resolved": "http://192.168.2.127:4873/china-area-data/-/china-area-data-5.0.1.tgz", + "integrity": "sha512-BQDPpiv5Nn+018ekcJK2oSD9PAD+E1bvXB0wgabc//dFVS/KvRqCgg0QOEUt3vBkx9XzB5a9BmkJCEZDBxVjVw==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "5.0.0", "dev": true, @@ -2095,6 +2136,12 @@ "typescript": ">=5" } }, + "node_modules/cropperjs": { + "version": "1.6.2", + "resolved": "http://192.168.2.127:4873/cropperjs/-/cropperjs-1.6.2.tgz", + "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -2221,6 +2268,16 @@ "node": ">= 0.4" } }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "http://192.168.2.127:4873/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -5597,6 +5654,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "http://192.168.2.127:4873/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "dev": true, @@ -5840,6 +5903,15 @@ "eslint": ">=6.0.0" } }, + "node_modules/vue-icons-plus": { + "version": "0.1.8", + "resolved": "http://192.168.2.127:4873/vue-icons-plus/-/vue-icons-plus-0.1.8.tgz", + "integrity": "sha512-Xc4hDsD/oP9waSUf44nSaFBhUPo+QkpKclx0S7//5BRACpXymctbit02epek0VRW6nb81pR486XmxPP/ofm2yQ==", + "license": "MIT", + "peerDependencies": { + "vue": ">=2.7.0" + } + }, "node_modules/vue-router": { "version": "4.6.4", "license": "MIT", @@ -6064,6 +6136,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "http://192.168.2.127:4873/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } } } } diff --git a/package.json b/package.json index 85b4782..d5e0930 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,15 @@ "type": "module", "scripts": { "serve": "vite", + "serve:prod": "vite --mode production", "build:dev": "vue-tsc --noEmit && vite build --mode development", "build:test": "vue-tsc --noEmit && vite build --mode test", "build:prod": "vue-tsc --noEmit && vite build --mode production", + "test": "vitest", + "test:run": "vitest run", + "test:coverage": "vitest run --coverage", "preview": "vite preview", + "preview:prod": "pnpm build:prod && vite preview", "type-check": "vue-tsc --noEmit", "lint": "eslint src --ext .js,.ts,.tsx", "lint:fix": "eslint src --ext .js,.ts,.tsx --fix", @@ -18,11 +23,18 @@ }, "dependencies": { "@ant-design/icons-vue": "^7.0.1", + "@types/big.js": "^7.0.0", + "@yp-component/root": "^0.0.48", "ant-design-vue": "^4.0.0", + "big.js": "^7.0.1", + "china-area-data": "^5.0.1", + "dayjs": "^1.11.21", + "echarts": "^5.6.0", "express": "^5.2.1", "html2canvas": "^1.4.1", "qs": "^6.15.0", "vue": "^3.3.0", + "vue-icons-plus": "^0.1.8", "vue-router": "^4.2.0" }, "devDependencies": { @@ -35,17 +47,21 @@ "@typescript-eslint/parser": "^6.0.0", "@vitejs/plugin-vue": "^4.5.0", "@vitejs/plugin-vue-jsx": "^4.0.0", + "@vitest/coverage-v8": "2.1.9", + "@vue/test-utils": "^2.4.11", "eslint": "^8.0.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-vue": "^9.0.0", "husky": "^9.1.7", + "jsdom": "^19.0.0", "less": "^4.6.4", "lint-staged": "^15.0.0", "prettier": "^3.0.0", "typescript": "^5.3.0", "vite": "^5.0.0", "vite-plugin-remove-console": "^2.2.0", + "vitest": "^2.1.9", "vue-tsc": "^2.0.0" }, "lint-staged": { diff --git a/permission_list.txt b/permission_list.txt new file mode 100644 index 0000000..2596644 --- /dev/null +++ b/permission_list.txt @@ -0,0 +1,57 @@ +# ============================================================ +# CPMS 运营平台 - 权限标识字符串列表 +# 生成规则: +# 1. 每个页面都有一个 "页面名-查看" 的默认权限 +# 2. 表格操作列中的按钮生成对应操作权限 +# 3. 同一关键字的两个状态按钮(如上架/下架、启用/禁用)合并为一个权限 +# 4. 筛选栏中有"新增"按钮的页面,生成新增权限 +# 5. 标识为英文,附带中文说明(含按钮所在位置) +# ============================================================ + +# ─── 赛事管理模块 ───────────────────────────────────────── + +events.list.view # 赛事列表-查看(页面默认查看权限) +events.list.toggle_status # 赛事列表-上架/下架(表格操作列,根据赛事状态显示"上架"或"下架"按钮,合并为一个权限) +events.regulation.view # 赛事规程-查看(赛事列表表格中的"赛事规程"列,点击"查看"链接) + +events.orders.view # 订单管理-查看(页面默认查看权限) +events.orders.refund # 订单管理-重新退款(表格操作列,退款状态为3时显示"重新退款"按钮) + +events.users.view # 用户列表-查看(页面默认查看权限) +events.users.toggle_status # 用户列表-启用/禁用(表格操作列,根据用户状态显示"启用"或"禁用"按钮,合并为一个权限) + +events.banner.view # Banner配置-查看(页面默认查看权限) +events.banner.add # Banner配置-新增(筛选栏中的"新增"按钮) +events.banner.edit # Banner配置-编辑(表格操作列中的"编辑"按钮) +events.banner.toggle_status # Banner配置-启用/禁用(表格操作列,根据状态显示"启用"或"禁用"按钮,合并为一个权限) +events.banner.delete # Banner配置-删除(表格操作列中的"删除"按钮,红色危险样式) + +events.logs.view # 赛事操作日志-查看(页面默认查看权限,无操作列按钮) + +# ─── 账务管理模块 ───────────────────────────────────────── + +finance.withdraw.view # 提现申请-查看(页面默认查看权限) +finance.withdraw.audit # 提现申请-审核(表格操作列,审核状态为"待审核"时显示"审核"按钮) + +finance.wallet.view # 用户钱包-查看(页面默认查看权限) +finance.wallet.detail # 用户钱包-查看明细(表格操作列中的"明细"按钮,打开交易明细弹窗) + +finance.payments.view # 支付流水-查看(页面默认查看权限,无操作列按钮,仅展示数据+金额汇总) + +finance.reports.view # 账务报表-查看(页面默认查看权限,无操作列按钮,展示统计卡+趋势图+明细表) + +# ─── 系统管理模块 ───────────────────────────────────────── + +system.users.view # 用户管理-查看(页面默认查看权限) +system.users.add # 用户管理-新增(筛选栏中的"新增"按钮) +system.users.edit # 用户管理-编辑(表格操作列中的"编辑"按钮) +system.users.toggle_status # 用户管理-启用/禁用(表格操作列,根据用户状态显示"启用"或"禁用"按钮,合并为一个权限) + +system.roles.view # 角色权限-查看(页面默认查看权限) +system.roles.add # 角色权限-新增(筛选栏中的"新增角色"按钮) +system.roles.edit # 角色权限-编辑(表格操作列中的"编辑"按钮) +system.roles.delete # 角色权限-删除(表格操作列中的"删除"按钮,红色危险样式) + +system.logs.view # 操作日志-查看(页面默认查看权限,无操作列按钮) + +system.params.view # 参数配置-查看 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e0b402d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5537 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ant-design/icons-vue': + specifier: ^7.0.1 + version: 7.0.1(vue@3.5.40(typescript@5.9.3)) + '@types/big.js': + specifier: ^7.0.0 + version: 7.0.0 + '@yp-component/root': + specifier: ^0.0.48 + version: 0.0.48(typescript@5.9.3) + ant-design-vue: + specifier: ^4.0.0 + version: 4.2.6(vue@3.5.40(typescript@5.9.3)) + big.js: + specifier: ^7.0.1 + version: 7.0.1 + china-area-data: + specifier: ^5.0.1 + version: 5.0.1 + dayjs: + specifier: ^1.11.21 + version: 1.11.21 + echarts: + specifier: ^5.6.0 + version: 5.6.0 + express: + specifier: ^5.2.1 + version: 5.2.1 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 + qs: + specifier: ^6.15.0 + version: 6.15.3 + vue: + specifier: ^3.3.0 + version: 3.5.40(typescript@5.9.3) + vue-icons-plus: + specifier: ^0.1.8 + version: 0.1.8(vue@3.5.40(typescript@5.9.3)) + vue-router: + specifier: ^4.2.0 + version: 4.6.4(vue@3.5.40(typescript@5.9.3)) + devDependencies: + '@commitlint/cli': + specifier: ^20.4.3 + version: 20.5.3(@types/node@20.19.43)(conventional-commits-parser@6.4.0)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: ^20.4.3 + version: 20.5.3 + '@rollup/plugin-strip': + specifier: ^3.0.4 + version: 3.0.4(rollup@4.62.2) + '@types/node': + specifier: ^20.10.0 + version: 20.19.43 + '@types/qs': + specifier: ^6.15.0 + version: 6.15.1 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@vitejs/plugin-vue': + specifier: ^4.5.0 + version: 4.6.2(vite@5.4.21(@types/node@20.19.43)(less@4.8.0))(vue@3.5.40(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': + specifier: ^4.0.0 + version: 4.2.0(vite@5.4.21(@types/node@20.19.43)(less@4.8.0))(vue@3.5.40(typescript@5.9.3)) + '@vitest/coverage-v8': + specifier: 2.1.9 + version: 2.1.9(vitest@2.1.9(@types/node@20.19.43)(jsdom@19.0.0)(less@4.8.0)) + '@vue/test-utils': + specifier: ^2.4.11 + version: 2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.40(typescript@5.9.3)) + eslint: + specifier: ^8.0.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.1.2(eslint@8.57.1) + eslint-plugin-prettier: + specifier: ^5.0.0 + version: 5.5.6(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.9.6) + eslint-plugin-vue: + specifier: ^9.0.0 + version: 9.33.0(eslint@8.57.1) + husky: + specifier: ^9.1.7 + version: 9.1.7 + jsdom: + specifier: ^19.0.0 + version: 19.0.0 + less: + specifier: ^4.6.4 + version: 4.8.0 + lint-staged: + specifier: ^15.0.0 + version: 15.5.2 + prettier: + specifier: ^3.0.0 + version: 3.9.6 + typescript: + specifier: ^5.3.0 + version: 5.9.3 + vite: + specifier: ^5.0.0 + version: 5.4.21(@types/node@20.19.43)(less@4.8.0) + vite-plugin-remove-console: + specifier: ^2.2.0 + version: 2.2.0 + vitest: + specifier: ^2.1.9 + version: 2.1.9(@types/node@20.19.43)(jsdom@19.0.0)(less@4.8.0) + vue-tsc: + specifier: ^2.0.0 + version: 2.2.12(typescript@5.9.3) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@ant-design/colors@6.0.0': + resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==} + + '@ant-design/icons-svg@4.5.0': + resolution: {integrity: sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==} + + '@ant-design/icons-vue@7.0.1': + resolution: {integrity: sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==} + peerDependencies: + vue: '>=3.0.3' + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@commitlint/cli@20.5.3': + resolution: {integrity: sha512-OJdL0EXWD5y9LPa0nr/geOwzaS8BsdaybKkcloB0JgsguGxNv2R+hC2FTPqrAcprg35zF33KOQerY0x8W1aesA==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@20.5.3': + resolution: {integrity: sha512-j34Qqeaa152chJgz2ysyk0BCpHenJn1lV0Rx0VXf8k3ccQcED+48EZrzMvo9jLmJUyBrrBwvu89I+2er4gW7QQ==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@20.5.0': + resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==} + engines: {node: '>=v18'} + + '@commitlint/ensure@20.5.3': + resolution: {integrity: sha512-4i4AgNvH62owG9MwSiWKrle7HGNpBHHdLnWFIp5fTsHUYe5kRuh15t08L/0pdbbrRk8JKXQxxN4hZQcn+szkrw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@20.0.0': + resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} + engines: {node: '>=v18'} + + '@commitlint/format@20.5.0': + resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@20.5.0': + resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==} + engines: {node: '>=v18'} + + '@commitlint/lint@20.5.3': + resolution: {integrity: sha512-M7JbWBNr2gXKaPc4i/KipsuW1gkDHpj35KPjWtKy3Z+2AQw5wu1gBi1LIO0uoaij67CqY4K8PxPZSGens4evCw==} + engines: {node: '>=v18'} + + '@commitlint/load@20.5.3': + resolution: {integrity: sha512-1FDZWuKyu98Myb8i7Tp31jPU2rZpOwAdYRyJcy2KoGg7Xk2A+bgHN8smhMaaNSNkmE8fwt53BokywZq8Gv/5XQ==} + engines: {node: '>=v18'} + + '@commitlint/message@20.4.3': + resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} + engines: {node: '>=v18'} + + '@commitlint/parse@20.5.0': + resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==} + engines: {node: '>=v18'} + + '@commitlint/read@20.5.0': + resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@20.5.3': + resolution: {integrity: sha512-+ogW9v/u9JqpvAgTrLra/YTFo0KkjU6iNblF89pPsj4NebNc+DAWctsludwezI8YnsjBmfHpApSwcXprN/f/ew==} + engines: {node: '>=v18'} + + '@commitlint/rules@20.5.3': + resolution: {integrity: sha512-MPlMnb9D3wbszYMp+1hPtuhtPJndRo6I6yfkZVA4+jR8w7Kqp0u2u/Y+gzbaItx5Lltq5rw7FSZQWJMoXUC4NQ==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@20.0.0': + resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@20.4.3': + resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} + engines: {node: '>=v18'} + + '@commitlint/types@20.5.0': + resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==} + engines: {node: '>=v18'} + + '@conventional-changelog/git-client@2.7.0': + resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} + engines: {node: '>=18'} + peerDependencies: + conventional-commits-filter: ^5.0.0 + conventional-commits-parser: ^6.4.0 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/unitless@0.8.1': + resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.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/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@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==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/plugin-strip@3.0.4': + resolution: {integrity: sha512-LDRV49ZaavxUo2YoKKMQjCxzCxugu1rCPQa0lDYBOWLj6vtzBMr8DcoJjsmg+s450RbKbe3qI9ZLaSO+O1oNbg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + + '@simonwep/pickr@1.8.2': + resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==} + + '@simple-libs/child-process-utils@1.0.2': + resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} + engines: {node: '>=18'} + + '@simple-libs/stream-utils@1.2.0': + resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} + engines: {node: '>=18'} + + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + engines: {node: '>= 10'} + + '@types/big.js@7.0.0': + resolution: {integrity: sha512-WfAGp7IbJvyB8EmWK4tJD24rJRAL6uVbw3LV/hJntFNam+os9KWKj0PzXo8rRRpjupYK8U0M8FoBB8dBhWF2dg==} + + '@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/node@20.19.43': + resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@typescript-eslint/eslint-plugin@6.21.0': + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/type-utils@6.21.0': + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@6.21.0': + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@vitejs/plugin-vue-jsx@4.2.0': + resolution: {integrity: sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@4.6.2': + resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.2.25 + + '@vitest/coverage-v8@2.1.9': + resolution: {integrity: sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==} + peerDependencies: + '@vitest/browser': 2.1.9 + vitest: 2.1.9 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} + + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + + '@vue/babel-helper-vue-transform-on@1.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.40': + resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + + '@vue/compiler-dom@3.5.40': + resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + + '@vue/compiler-sfc@3.5.40': + resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + + '@vue/compiler-ssr@3.5.40': + resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.40': + resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==} + + '@vue/runtime-core@3.5.40': + resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==} + + '@vue/runtime-dom@3.5.40': + resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==} + + '@vue/server-renderer@3.5.40': + resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==} + + '@vue/shared@3.5.40': + resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + + '@vue/test-utils@2.4.11': + resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==} + peerDependencies: + '@vue/compiler-dom': 3.x + '@vue/server-renderer': 3.x + vue: 3.x + peerDependenciesMeta: + '@vue/server-renderer': + optional: true + + '@yp-component/root@0.0.48': + resolution: {integrity: sha512-TOp8adAob3pMSfecQjEC4uVNCGqNWcShDjSQysd4pYY34i5ldFLRGSiJdpLsFG1xKkcSudiqEc/Si/ZfYoDSqA==} + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@6.0.0: + resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ant-design-vue@4.2.6: + resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==} + engines: {node: '>=12.22.0'} + peerDependencies: + vue: '>=3.2.0' + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-tree-filter@2.1.0: + resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + + baseline-browser-mapping@2.11.1: + resolution: {integrity: sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==} + engines: {node: '>=6.0.0'} + hasBin: true + + big.js@7.0.1: + resolution: {integrity: sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg==} + + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + engines: {node: '>=18'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-process-hrtime@1.0.0: + resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + china-area-data@5.0.1: + resolution: {integrity: sha512-BQDPpiv5Nn+018ekcJK2oSD9PAD+E1bvXB0wgabc//dFVS/KvRqCgg0QOEUt3vBkx9XzB5a9BmkJCEZDBxVjVw==} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + conventional-changelog-angular@8.3.1: + resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==} + engines: {node: '>=18'} + + conventional-changelog-conventionalcommits@9.3.1: + resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==} + engines: {node: '>=18'} + + conventional-commits-parser@6.4.0: + resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==} + engines: {node: '>=18'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + core-js@3.49.0: + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + + cosmiconfig-typescript-loader@6.3.0: + resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cropperjs@1.6.2: + resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-align@1.12.4: + resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} + + dom-scroll-into-view@2.0.1: + resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==} + + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + engines: {node: '>=14'} + hasBin: true + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.395: + resolution: {integrity: sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + 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==} + + 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-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.49.0: + resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.5.6: + resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@9.33.0: + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.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'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.4: + resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.4.3: + resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + git-raw-commits@5.0.1: + resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} + engines: {node: '>=18'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + 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.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + 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'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + 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-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@3.0.1: + resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.8: + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsdom@19.0.0: + resolution: {integrity: sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==} + engines: {node: '>=12'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-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 + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + less@4.8.0: + resolution: {integrity: sha512-7Y7DJBMbsW29UGjOG6NGvxQEx71AaDcrryBwYCMaFwn0kj8FkSueKN9r9WexVOetKEhXh38kL++WJncfkEpS+g==} + engines: {node: '>=18'} + hasBin: true + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.5.2: + resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-dir@5.1.0: + resolution: {integrity: sha512-IfpFq6UM39dUNiphpA6uDezNx/AvWyhwfICWPR3t1VspkgkMZrL+Rk1RbN1bx+aeNYwOrqGJgEgV3yotk+ZUVw==} + engines: {node: '>=18'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@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-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + 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.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanopop@2.4.2: + resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@2.9.1: + resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + + needle@3.5.0: + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nwsapi@2.2.24: + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + 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-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pidtree@0.6.1: + resolution: {integrity: sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==} + engines: {node: '>=0.10'} + hasBin: true + + postcss-selector-parser@6.1.4: + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + engines: {node: '>=4'} + + postcss@8.5.21: + resolution: {integrity: sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + probe-image-size@7.3.0: + resolution: {integrity: sha512-7CaDeBwiAbh6ohXsvLbAZhO7wzsZAmaevfxe39qvCwRh8LyaZfDlBGGLU1CCTgrTLtCOdwBBhjOrIHaIIimHfQ==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + engines: {node: '>=0.6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + 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'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + 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==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + 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'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stream-parser@0.3.1: + resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + engines: {node: ^14.18.0 || >=16.0.0} + + test-exclude@7.0.2: + resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} + engines: {node: '>=18'} + + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite-plugin-remove-console@2.2.0: + resolution: {integrity: sha512-qgjh5pz75MdE9Kzs8J0kBwaCfifHV0ezRbB9rpGsIOxam+ilcGV7WOk91vFJXquzRmiKrFh3Hxlh0JJWAmXTbQ==} + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-component-type-helpers@3.3.9: + resolution: {integrity: sha512-3c/UfMe0SqyEfcGTyH7mfshHagJ9QTCbppCb0/uGpHZpFug7+If3GeGZN7I0YheKEExemx3xldQPoO7PQSOLQg==} + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-icons-plus@0.1.8: + resolution: {integrity: sha512-Xc4hDsD/oP9waSUf44nSaFBhUPo+QkpKclx0S7//5BRACpXymctbit02epek0VRW6nb81pR486XmxPP/ofm2yQ==} + peerDependencies: + vue: '>=2.7.0' + + vue-router@4.6.4: + resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} + peerDependencies: + vue: ^3.5.0 + + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue-types@3.0.2: + resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==} + engines: {node: '>=10.15.0'} + peerDependencies: + vue: ^3.0.0 + + vue@3.5.40: + resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-hr-time@1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. + + w3c-xmlserializer@3.0.0: + resolution: {integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==} + engines: {node: '>=12'} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url@10.0.0: + resolution: {integrity: sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==} + engines: {node: '>=12'} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@ant-design/colors@6.0.0': + dependencies: + '@ctrl/tinycolor': 3.6.1 + + '@ant-design/icons-svg@4.5.0': {} + + '@ant-design/icons-vue@7.0.1(vue@3.5.40(typescript@5.9.3))': + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-svg': 4.5.0 + vue: 3.5.40(typescript@5.9.3) + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@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.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.7 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@0.2.3': {} + + '@commitlint/cli@20.5.3(@types/node@20.19.43)(conventional-commits-parser@6.4.0)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 20.5.0 + '@commitlint/lint': 20.5.3 + '@commitlint/load': 20.5.3(@types/node@20.19.43)(typescript@5.9.3) + '@commitlint/read': 20.5.0(conventional-commits-parser@6.4.0) + '@commitlint/types': 20.5.0 + tinyexec: 1.2.4 + yargs: 17.7.3 + transitivePeerDependencies: + - '@types/node' + - conventional-commits-filter + - conventional-commits-parser + - typescript + + '@commitlint/config-conventional@20.5.3': + dependencies: + '@commitlint/types': 20.5.0 + conventional-changelog-conventionalcommits: 9.3.1 + + '@commitlint/config-validator@20.5.0': + dependencies: + '@commitlint/types': 20.5.0 + ajv: 8.20.0 + + '@commitlint/ensure@20.5.3': + dependencies: + '@commitlint/types': 20.5.0 + es-toolkit: 1.49.0 + + '@commitlint/execute-rule@20.0.0': {} + + '@commitlint/format@20.5.0': + dependencies: + '@commitlint/types': 20.5.0 + picocolors: 1.1.1 + + '@commitlint/is-ignored@20.5.0': + dependencies: + '@commitlint/types': 20.5.0 + semver: 7.8.5 + + '@commitlint/lint@20.5.3': + dependencies: + '@commitlint/is-ignored': 20.5.0 + '@commitlint/parse': 20.5.0 + '@commitlint/rules': 20.5.3 + '@commitlint/types': 20.5.0 + + '@commitlint/load@20.5.3(@types/node@20.19.43)(typescript@5.9.3)': + dependencies: + '@commitlint/config-validator': 20.5.0 + '@commitlint/execute-rule': 20.0.0 + '@commitlint/resolve-extends': 20.5.3 + '@commitlint/types': 20.5.0 + cosmiconfig: 9.0.2(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@20.19.43)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3) + es-toolkit: 1.49.0 + is-plain-obj: 4.1.0 + picocolors: 1.1.1 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@20.4.3': {} + + '@commitlint/parse@20.5.0': + dependencies: + '@commitlint/types': 20.5.0 + conventional-changelog-angular: 8.3.1 + conventional-commits-parser: 6.4.0 + + '@commitlint/read@20.5.0(conventional-commits-parser@6.4.0)': + dependencies: + '@commitlint/top-level': 20.4.3 + '@commitlint/types': 20.5.0 + git-raw-commits: 5.0.1(conventional-commits-parser@6.4.0) + minimist: 1.2.8 + tinyexec: 1.2.4 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser + + '@commitlint/resolve-extends@20.5.3': + dependencies: + '@commitlint/config-validator': 20.5.0 + '@commitlint/types': 20.5.0 + es-toolkit: 1.49.0 + global-directory: 5.0.0 + import-meta-resolve: 4.2.0 + resolve-from: 5.0.0 + + '@commitlint/rules@20.5.3': + dependencies: + '@commitlint/ensure': 20.5.3 + '@commitlint/message': 20.4.3 + '@commitlint/to-lines': 20.0.0 + '@commitlint/types': 20.5.0 + + '@commitlint/to-lines@20.0.0': {} + + '@commitlint/top-level@20.4.3': + dependencies: + escalade: 3.2.0 + + '@commitlint/types@20.5.0': + dependencies: + conventional-commits-parser: 6.4.0 + picocolors: 1.1.1 + + '@conventional-changelog/git-client@2.7.0(conventional-commits-parser@6.4.0)': + dependencies: + '@simple-libs/child-process-utils': 1.0.2 + '@simple-libs/stream-utils': 1.2.0 + semver: 7.8.5 + optionalDependencies: + conventional-commits-parser: 6.4.0 + + '@ctrl/tinycolor@3.6.1': {} + + '@emotion/hash@0.9.2': {} + + '@emotion/unitless@0.8.1': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.6': {} + + '@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 + + '@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.20.1 + + '@one-ini/wasm@0.1.1': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.3.6': {} + + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/plugin-strip@3.0.4(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.5 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@simonwep/pickr@1.8.2': + dependencies: + core-js: 3.49.0 + nanopop: 2.4.2 + + '@simple-libs/child-process-utils@1.0.2': + dependencies: + '@simple-libs/stream-utils': 1.2.0 + + '@simple-libs/stream-utils@1.2.0': {} + + '@tootallnate/once@2.0.1': {} + + '@types/big.js@7.0.0': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@20.19.43': + dependencies: + undici-types: 6.21.0 + + '@types/qs@6.15.1': {} + + '@types/semver@7.7.1': {} + + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.8.5 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3 + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.8.5 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + eslint: 8.57.1 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.3.3': {} + + '@vitejs/plugin-vue-jsx@4.2.0(vite@5.4.21(@types/node@20.19.43)(less@4.8.0))(vue@3.5.40(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.1 + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.7) + vite: 5.4.21(@types/node@20.19.43)(less@4.8.0) + vue: 3.5.40(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@4.6.2(vite@5.4.21(@types/node@20.19.43)(less@4.8.0))(vue@3.5.40(typescript@5.9.3))': + dependencies: + vite: 5.4.21(@types/node@20.19.43)(less@4.8.0) + vue: 3.5.40(typescript@5.9.3) + + '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.19.43)(jsdom@19.0.0)(less@4.8.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magic-string: 0.30.21 + magicast: 0.3.5 + std-env: 3.10.0 + test-exclude: 7.0.2 + tinyrainbow: 1.2.0 + vitest: 2.1.9(@types/node@20.19.43)(jsdom@19.0.0)(less@4.8.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.43)(less@4.8.0))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@20.19.43)(less@4.8.0) + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.21 + pathe: 1.1.2 + + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.2.1 + tinyrainbow: 1.2.0 + + '@volar/language-core@2.4.15': + dependencies: + '@volar/source-map': 2.4.15 + + '@volar/source-map@2.4.15': {} + + '@volar/typescript@2.4.15': + dependencies: + '@volar/language-core': 2.4.15 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/babel-helper-vue-transform-on@1.5.0': {} + + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.29.7)': + dependencies: + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@vue/babel-helper-vue-transform-on': 1.5.0 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.29.7) + '@vue/shared': 3.5.40 + optionalDependencies: + '@babel/core': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.29.7)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/parser': 7.29.7 + '@vue/compiler-sfc': 3.5.40 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.40 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.40': + dependencies: + '@vue/compiler-core': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/compiler-sfc@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.40 + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-ssr': 3.5.40 + '@vue/shared': 3.5.40 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.21 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.40': + dependencies: + '@vue/compiler-dom': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/language-core@2.2.12(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.40 + alien-signals: 1.0.13 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.9.3 + + '@vue/reactivity@3.5.40': + dependencies: + '@vue/shared': 3.5.40 + + '@vue/runtime-core@3.5.40': + dependencies: + '@vue/reactivity': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/runtime-dom@3.5.40': + dependencies: + '@vue/reactivity': 3.5.40 + '@vue/runtime-core': 3.5.40 + '@vue/shared': 3.5.40 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.40': + dependencies: + '@vue/compiler-ssr': 3.5.40 + '@vue/runtime-dom': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/shared@3.5.40': {} + + '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.40(typescript@5.9.3))': + dependencies: + '@vue/compiler-dom': 3.5.40 + js-beautify: 1.15.4 + vue: 3.5.40(typescript@5.9.3) + vue-component-type-helpers: 3.3.9 + optionalDependencies: + '@vue/server-renderer': 3.5.40 + + '@yp-component/root@0.0.48(typescript@5.9.3)': + dependencies: + cropperjs: 1.6.2 + vue: 3.5.40(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + abab@2.0.6: {} + + abbrev@2.0.0: {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-globals@6.0.0: + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn-walk@7.2.0: {} + + acorn@7.4.1: {} + + acorn@8.17.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ajv@6.15.0: + 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.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.4 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + alien-signals@1.0.13: {} + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + ant-design-vue@4.2.6(vue@3.5.40(typescript@5.9.3)): + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-vue': 7.0.1(vue@3.5.40(typescript@5.9.3)) + '@babel/runtime': 7.29.7 + '@ctrl/tinycolor': 3.6.1 + '@emotion/hash': 0.9.2 + '@emotion/unitless': 0.8.1 + '@simonwep/pickr': 1.8.2 + array-tree-filter: 2.1.0 + async-validator: 4.2.5 + csstype: 3.2.3 + dayjs: 1.11.21 + dom-align: 1.12.4 + dom-scroll-into-view: 2.0.1 + lodash: 4.18.1 + lodash-es: 4.18.1 + resize-observer-polyfill: 1.5.1 + scroll-into-view-if-needed: 2.2.31 + shallow-equal: 1.2.1 + stylis: 4.4.0 + throttle-debounce: 5.0.2 + vue: 3.5.40(typescript@5.9.3) + vue-types: 3.0.2(vue@3.5.40(typescript@5.9.3)) + warning: 4.0.3 + + argparse@2.0.1: {} + + array-ify@1.0.0: {} + + array-tree-filter@2.1.0: {} + + array-union@2.1.0: {} + + assertion-error@2.0.1: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-arraybuffer@1.0.2: {} + + baseline-browser-mapping@2.11.1: {} + + big.js@7.0.1: {} + + body-parser@2.3.0: + dependencies: + bytes: 3.1.2 + content-type: 2.0.0 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + on-finished: 2.4.1 + qs: 6.15.3 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + boolbase@1.0.0: {} + + brace-expansion@1.1.16: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-process-hrtime@1.0.0: {} + + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.1 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.395 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + + bytes@3.1.2: {} + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001806: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + check-error@2.1.3: {} + + china-area-data@5.0.1: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@10.0.1: {} + + commander@13.1.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compute-scroll-into-view@1.0.20: {} + + concat-map@0.0.1: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.0.0: {} + + conventional-changelog-angular@8.3.1: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@9.3.1: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@6.4.0: + dependencies: + '@simple-libs/stream-utils': 1.2.0 + meow: 13.2.0 + + convert-source-map@2.0.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + core-js@3.49.0: {} + + cosmiconfig-typescript-loader@6.3.0(@types/node@20.19.43)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 20.19.43 + cosmiconfig: 9.0.2(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + + cosmiconfig@9.0.2(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + cropperjs@1.6.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + + cssesc@3.0.0: {} + + cssom@0.3.8: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + + csstype@3.2.3: {} + + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + + dayjs@1.11.21: {} + + de-indent@1.0.2: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + optional: true + + debug@3.2.7: + dependencies: + ms: 2.1.3 + optional: true + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-align@1.12.4: {} + + dom-scroll-into-view@2.0.1: {} + + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + echarts@5.6.0: + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + + editorconfig@1.0.7: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.9 + semver: 7.8.5 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.395: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + entities@7.0.1: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.2: + 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.4 + + es-toolkit@1.49.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@9.1.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-prettier@5.5.6(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.9.6): + dependencies: + eslint: 8.57.1 + prettier: 3.9.6 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.13 + optionalDependencies: + eslint-config-prettier: 9.1.2(eslint@8.57.1) + + eslint-plugin-vue@9.33.0(eslint@8.57.1): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1) + eslint: 8.57.1 + globals: 13.24.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.4 + semver: 7.8.5 + vue-eslint-parser: 9.4.3(eslint@8.57.1) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.3 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.3.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + 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.9 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@5.0.4: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expect-type@1.4.0: {} + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.3.0 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.4: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.4.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.4.3: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.6.0: {} + + 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.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-stream@8.0.1: {} + + git-raw-commits@5.0.1(conventional-commits-parser@6.4.0): + dependencies: + '@conventional-changelog/git-client': 2.7.0(conventional-commits-parser@6.4.0) + meow: 13.2.0 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-directory@5.0.0: + dependencies: + ini: 6.0.0 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: + optional: true + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-escaper@2.0.2: {} + + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.1 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + optional: true + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + 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: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@6.0.0: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@3.0.1: {} + + is-potential-custom-element-name@1.0.1: {} + + is-promise@4.0.0: {} + + is-stream@3.0.0: {} + + is-what@4.1.16: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@2.6.1: {} + + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.7 + glob: 10.4.5 + js-cookie: 3.0.8 + nopt: 7.2.1 + + js-cookie@3.0.8: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsdom@19.0.0: + dependencies: + abab: 2.0.6 + acorn: 8.17.0 + acorn-globals: 6.0.0 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.6 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.24 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 3.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 10.0.0 + ws: 8.21.3 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + 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: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + less@4.8.0: + dependencies: + copy-anything: 3.0.5 + parse-node-version: 1.0.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + make-dir: 5.1.0 + mime: 1.6.0 + needle: 3.5.0 + probe-image-size: 7.3.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.5.2: + dependencies: + chalk: 5.6.2 + commander: 13.1.0 + debug: 4.4.3 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + pidtree: 0.6.1 + string-argv: 0.3.2 + yaml: 2.9.0 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.18.1: {} + + lodash.merge@4.6.2: {} + + lodash@4.18.1: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + make-dir@5.1.0: + optional: true + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + meow@13.2.0: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: + optional: true + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.16 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.1.2 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.2 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + ms@2.0.0: + optional: true + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nanoid@3.3.16: {} + + nanopop@2.4.2: {} + + natural-compare@1.4.0: {} + + needle@2.9.1: + dependencies: + debug: 3.2.7 + iconv-lite: 0.4.24 + sax: 1.6.0 + transitivePeerDependencies: + - supports-color + optional: true + + needle@3.5.0: + dependencies: + iconv-lite: 0.6.3 + sax: 1.6.0 + optional: true + + negotiator@1.0.0: {} + + node-releases@2.0.51: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nwsapi@2.2.24: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + 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: {} + + parse5@6.0.1: {} + + parseurl@1.3.3: {} + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-to-regexp@8.4.2: {} + + path-type@4.0.0: {} + + pathe@1.1.2: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.5: {} + + pidtree@0.6.1: {} + + postcss-selector-parser@6.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.21: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + prettier@3.9.6: {} + + probe-image-size@7.3.0: + dependencies: + lodash.merge: 4.6.2 + needle: 2.9.1 + stream-parser: 0.3.1 + transitivePeerDependencies: + - supports-color + optional: true + + proto-list@1.2.4: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + prr@1.0.1: + optional: true + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + qs@6.15.3: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + range-parser@1.3.0: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + unpipe: 1.0.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safer-buffer@2.1.2: {} + + sax@1.6.0: + optional: true + + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + semver@6.3.1: {} + + semver@7.8.5: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.3.0 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shallow-equal@1.2.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + source-map-js@1.2.1: {} + + source-map@0.6.1: + optional: true + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + stream-parser@0.3.1: + dependencies: + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + optional: true + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + stylis@4.4.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + synckit@0.11.13: + dependencies: + '@pkgr/core': 0.3.6 + + test-exclude@7.0.2: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 10.4.5 + minimatch: 10.2.6 + + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + + text-table@0.2.0: {} + + throttle-debounce@5.0.2: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.2.4: {} + + tinypool@1.1.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.3.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + universalify@0.2.0: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + + vary@1.1.2: {} + + vite-node@2.1.9(@types/node@20.19.43)(less@4.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.21(@types/node@20.19.43)(less@4.8.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite-plugin-remove-console@2.2.0: {} + + vite@5.4.21(@types/node@20.19.43)(less@4.8.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.21 + rollup: 4.62.2 + optionalDependencies: + '@types/node': 20.19.43 + fsevents: 2.3.3 + less: 4.8.0 + + vitest@2.1.9(@types/node@20.19.43)(jsdom@19.0.0)(less@4.8.0): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.43)(less@4.8.0)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 1.1.2 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.21(@types/node@20.19.43)(less@4.8.0) + vite-node: 2.1.9(@types/node@20.19.43)(less@4.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.43 + jsdom: 19.0.0 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vscode-uri@3.1.0: {} + + vue-component-type-helpers@3.3.9: {} + + vue-eslint-parser@9.4.3(eslint@8.57.1): + dependencies: + debug: 4.4.3 + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + lodash: 4.18.1 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + vue-icons-plus@0.1.8(vue@3.5.40(typescript@5.9.3)): + dependencies: + vue: 3.5.40(typescript@5.9.3) + + vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.40(typescript@5.9.3) + + vue-tsc@2.2.12(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.9.3) + typescript: 5.9.3 + + vue-types@3.0.2(vue@3.5.40(typescript@5.9.3)): + dependencies: + is-plain-object: 3.0.1 + vue: 3.5.40(typescript@5.9.3) + + vue@3.5.40(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-sfc': 3.5.40 + '@vue/runtime-dom': 3.5.40 + '@vue/server-renderer': 3.5.40 + '@vue/shared': 3.5.40 + optionalDependencies: + typescript: 5.9.3 + + w3c-hr-time@1.0.2: + dependencies: + browser-process-hrtime: 1.0.0 + + w3c-xmlserializer@3.0.0: + dependencies: + xml-name-validator: 4.0.0 + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@3.0.0: {} + + whatwg-url@10.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + ws@8.21.3: {} + + xml-name-validator@4.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zrender@5.6.1: + dependencies: + tslib: 2.3.0 diff --git a/src/App.tsx b/src/App.tsx index f6f5c6d..e25c41f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,10 @@ import { defineComponent } from 'vue'; import { ConfigProvider } from 'ant-design-vue'; -import zhCN from 'ant-design-vue/es/locale/zh_CN'; +import zhCN from 'ant-design-vue/locale/zh_CN'; import { RouterView } from 'vue-router'; +import dayjs from 'dayjs'; +import 'dayjs/locale/zh-cn'; +dayjs.locale('zh-cn'); /** * 根组件 diff --git a/src/api/authCache.ts b/src/api/authCache.ts new file mode 100644 index 0000000..590fe53 --- /dev/null +++ b/src/api/authCache.ts @@ -0,0 +1,17 @@ +import type { AuthData } from './menu'; + +/** Promise 级缓存:并发调用共享同一个请求,失败时清除以便重试 */ +let authDataPromise: Promise | null = null; + +export function getAuthDataPromise() { + return authDataPromise; +} + +export function setAuthDataPromise(promise: Promise | null) { + authDataPromise = promise; +} + +/** 清除权限数据缓存(退出登录时调用,确保下次登录不读到旧数据) */ +export function clearAuthDataCache() { + authDataPromise = null; +} diff --git a/src/api/banner/index.ts b/src/api/banner/index.ts new file mode 100644 index 0000000..61c7918 --- /dev/null +++ b/src/api/banner/index.ts @@ -0,0 +1,50 @@ +/** + * Banner 管理 API + */ +import { get, post } from '@/utils/request'; +import type { + BannerListQueryParams, + BannerSaveParams, + BannerDelParams, + BannerActiveParams, + TournamentAdminBannerListVO, + ApiResult, +} from './types'; + +export type { + TournamentAdminBannerListVO, + BannerListQueryParams, + BannerSaveParams, + BannerDelParams, + BannerActiveParams, + ApiResult, +} from './types'; + +/** GET /manager/banner/list — Banner 列表 */ +export async function getBannerList( + params: BannerListQueryParams, +): Promise> { + return get('/manager/banner/list', params as Record); +} + +/** POST /manager/banner/del — 删除 Banner */ +export function delBanner(id: string): Promise>> { + return post('/manager/banner/del', { id }); +} + +/** POST /manager/banner/save — 新增 Banner */ +export function saveBanner(params: BannerSaveParams): Promise>> { + return post('/manager/banner/save', params); +} + +/** POST /manager/banner/update �� 编辑 Banner */ +export function updateBanner(params: BannerSaveParams): Promise>> { + return post('/manager/banner/update', params); +} + +/** POST /manager/banner/active — 启用/禁用 Banner */ +export function toggleBannerActive( + params: BannerActiveParams, +): Promise>> { + return post('/manager/banner/active', params); +} diff --git a/src/api/banner/types.ts b/src/api/banner/types.ts new file mode 100644 index 0000000..ad2ac86 --- /dev/null +++ b/src/api/banner/types.ts @@ -0,0 +1,53 @@ +/** + * Banner 管理 — 类型定义 + */ + +/** Banner 列表项 */ +export interface TournamentAdminBannerListVO { + id: string; + img: string; + title: string; + type: string; + params: string; + area: string; + showTimeBegin: string; + showTimeEnd: string; + status: string; + createDate: string; +} + +/** Banner 列表查询参数 */ +export interface BannerListQueryParams { + title?: string; + status?: string; +} + +/** Banner 新增/编辑请求 */ +export interface BannerSaveParams { + id?: string; + img: string; + title: string; + type: string; + params: string; + area: string; + showTimeBegin: string; + showTimeEnd: string; + sort: string; +} + +/** Banner 删除请求 */ +export interface BannerDelParams { + id: string; +} + +/** Banner 启用/禁用请求 */ +export interface BannerActiveParams { + id: string; + status: string; +} + +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/events/index.ts b/src/api/events/index.ts new file mode 100644 index 0000000..67f3ab5 --- /dev/null +++ b/src/api/events/index.ts @@ -0,0 +1,67 @@ +/** + * 赛事管理 API + * 注意:/admin 前缀已通过 VITE_API_BASE_URL 统一注入(见 .env.*),此文件中只写业务路径。 + */ +import { get, post } from '@/utils/request'; +import type { + EventListQueryParams, + SignupListQueryParams, + ToggleOnlineParams, + ApiResult, + PageData, + TournamentAdminVO, + TournamentAdminInfoVO, + TournamentAdminInfoSignupVO, +} from './types'; + +export type { + TournamentAdminVO, + TournamentAdminInfoVO, + TournamentAdminInfoSignupVO, + InnerCategoryVO, + EventListQueryParams, + SignupListQueryParams, + ToggleOnlineParams, + PageData, + ApiResult, +} from './types'; + +/** + * 赛事列表 - 列表分页 + * GET /admin/manager/tm/page → /manager/tm/page + */ +export async function getEventList( + params: EventListQueryParams, +): Promise>> { + return get('/manager/tm/page', params as Record); +} + +/** + * 赛事列表 - 赛事详情 + * GET /admin/manager/tm/info → /manager/tm/info + */ +export async function getEventDetail(id: string): Promise> { + return get('/manager/tm/info', { id }); +} + +/** + * 赛事列表 - 赛事详情 - 人员列表 + * GET /admin/manager/tm/info/signupPage → /manager/tm/info/signupPage + */ +export async function getSignupList( + params: SignupListQueryParams, +): Promise>> { + return get('/manager/tm/info/signupPage', params as Record); +} + +/** + * 赛事上架 / 下架 + * POST /admin/manager/tm/online → /manager/tm/online + * @param params.id 赛事ID + * @param params.online 0=下架,1=上架 + */ +export function toggleEventOnline( + params: ToggleOnlineParams, +): Promise>> { + return post('/manager/tm/online', params); +} diff --git a/src/api/events/types.ts b/src/api/events/types.ts new file mode 100644 index 0000000..d7e25cd --- /dev/null +++ b/src/api/events/types.ts @@ -0,0 +1,213 @@ +/** + * 赛事管理 — 类型定义 + * 对应 OpenAPI: /admin/manager/tm/page + */ + +/** 赛事列表项 */ +export interface TournamentAdminVO { + /** 赛事ID */ + id: string; + /** 赛事名称 */ + name: string; + /** 赛事开始时间 */ + startTimeBegin: string; + /** 赛事结束时间 */ + startTimeEnd: string; + /** 赛事省市区,场地区划名称 */ + venueArea: string; + /** 赛事地点名称 */ + venue: string; + /** 是否公开;0=非公开,1=公开 */ + isPublic: string; + /** 是否要求提供证件图片;0=无需,1=需要 */ + isRequireIdCardImg: string; + /** 是否要求提供证件号码;0=无需,1=需要 */ + isRequireIdCardNo: string; + /** 创建人昵称 */ + createNickname: string; + /** 创建人手机号 */ + createPhone: string; + /** 创建时间 */ + createTime: string; + /** + * 状态 + * 优先级:del_flag==1 → 3(已删除);online==0 → 4(已下架);否则取 status + * 0=报名中,1=进行中,2=已结束,3=已删除,4=已下架 + */ + status: string; + /** 报名人数 */ + signupCount: string; + /** 退赛人数 */ + dropoutCount: string; + /** 赛事规程 */ + rulesDescr: string; + /** 赛事规程图片 */ + rulesDescrImg: string; + /** 赛事规程图片数组 */ + rulesDescrImgList: string[]; +} + +/** 赛事列表查询参数 */ +export interface EventListQueryParams { + page?: string; + limit?: string; + /** 创建时间开始 yyyy-MM-dd */ + createDate?: string; + /** 创建时间结束 yyyy-MM-dd */ + createDateEnd?: string; + /** 赛事时间开始 yyyy-MM-dd */ + startDate?: string; + /** 赛事时间结束 yyyy-MM-dd */ + startDateEnd?: string; + /** 赛事名称 */ + name?: string; + /** 创建人昵称或手机号 */ + createName?: string; + /** + * 赛事状态 + * 不传=全部、0=报名中,1=进行中,2=已结束,3=已删除,4=已下架 + */ + status?: string; + /** 市级别城市名称 */ + areaName?: string; + /** 是否公开;0=非公开,1=公开 */ + isPublic?: string; + /** 是否要求提供证件图片;0=无需,1=需要 */ + idCardImg?: string; + /** 是否要求提供证件号码;0=无需,1=需要 */ + idCardNo?: string; +} + +/** 分页数据 */ +export interface PageData { + total: number; + list: T[]; +} + +/** 统一响应包裹 */ +export interface ApiResult { + /** 编码:200表示成功,其他值表示失败 */ + code: number | string; + /** 消息内容 */ + msg: string; + /** 响应数据 */ + data: T; +} + +// ============================================================ +// 赛事详情(对应 /manager/tm/info) +// ============================================================ + +/** 组别信息 */ +export interface InnerCategoryVO { + /** 组别ID */ + id: string; + /** 组别名称 */ + name: string; + /** 组别类型;1=单打,2=双打 */ + type: string; +} + +/** 赛事详情 */ +export interface TournamentAdminInfoVO { + /** 赛事名称 */ + name: string; + /** 赛事开始时间 */ + startTimeBegin: string; + /** 赛事结束时间 */ + startTimeEnd: string; + /** 赛事说明 */ + descr: string; + /** 报名开始时间 */ + signupTimeBegin: string; + /** 报名结束时间 */ + signupTimeEnd: string; + /** 赛事省市区 */ + venueArea: string; + /** 赛事地点名称 */ + venue: string; + /** 场地号 */ + venueNo: string; + /** 是否公开;0=非公开,1=公开 */ + isPublic: string; + /** 是否要求提供证件图片;0=无需,1=需要 */ + isRequireIdCardImg: string; + /** 是否要求提供证件号码;0=无需,1=需要 */ + isRequireIdCardNo: string; + /** 创建时间 */ + createTime: string; + /** 创建人昵称 */ + createNickname: string; + /** 报名人数 */ + signupCount: string; + /** 退赛人数 */ + dropoutCount: string; + /** 允许报名最大人数 */ + totalLimit: string; + /** 总对阵数 */ + matchTotalCount: string; + /** 已完成对阵数 */ + matchCount: string; + /** 赛事封面url */ + img: string; + /** 活动公告 */ + notice: string; + /** 组别列表,创建时间倒序 */ + categoryList: InnerCategoryVO[]; + /** 分组名称列表,格式 "小组名称(淘汰赛或循环赛)" */ + groupInfoList: string[]; +} + +// ============================================================ +// 人员列表(对应 /manager/tm/info/signupPage) +// ============================================================ + +/** 报名人员 */ +export interface TournamentAdminInfoSignupVO { + /** 选手名称 */ + realName: string; + /** 选手性别;1=男,2=女 */ + gender: string; + /** 选手手机号 */ + phone: string; + /** 选手身份证 */ + idCard: string; + /** 选手身份证图片 */ + idCardImg: string; + /** 选手身份证图片列表 */ + idCardImgList: string[]; +} + +/** 人员列表查询参数 */ +export interface SignupListQueryParams { + page: string; + limit: string; + /** 组别ID(必填) */ + categoryId: string; +} + +// ============================================================ +// 赛事规程(对应 /admin/manager/tm/ruleInfo) +// ============================================================ + +/** 赛事规程详情 */ +export interface TournamentAdminRuleInfoVO { + /** 赛事规程文字内容 */ + rulesDescr?: string; + /** 赛事规程图片 */ + rulesDescrImg?: string; + /** 赛事规程图片列表 */ + rulesDescrImgList?: string[]; +} + +// ============================================================ +// 上下架(对应 /manager/tm/online) +// ============================================================ + +/** 上下架请求参数 */ +export interface ToggleOnlineParams { + /** 赛事ID */ + id: string; + /** 0=下架,1=上架 */ + online: string; +} diff --git a/src/api/login/index.ts b/src/api/login/index.ts new file mode 100644 index 0000000..aff4797 --- /dev/null +++ b/src/api/login/index.ts @@ -0,0 +1,19 @@ +import { post } from '@/utils/request'; +import type { LoginResult } from './types'; + +export type { LoginResult } from './types'; + +export const login = (params: { phone: string; password: string }) => { + return post(`/op/login`, params) as Promise<{ + code: number | string; + msg: string; + data: LoginResult; + }>; +}; + +export const logout = () => { + return post(`/op/logout`) as Promise<{ + code: number | string; + msg: string; + }>; +}; diff --git a/src/api/login/types.ts b/src/api/login/types.ts new file mode 100644 index 0000000..d152867 --- /dev/null +++ b/src/api/login/types.ts @@ -0,0 +1,8 @@ +export interface LoginResult { + token: string; + phone?: string; + nickName?: string; + realName?: string; + userName?: string; + name?: string; +} diff --git a/src/api/logs/index.ts b/src/api/logs/index.ts new file mode 100644 index 0000000..9b02c83 --- /dev/null +++ b/src/api/logs/index.ts @@ -0,0 +1,36 @@ +/** + * 操作日志 API + */ +import { get } from '@/utils/request'; +import type { + OperationLogQueryParams, + SysOperationLogVO, + TournamentAdminOperationPageVO, + PageData, + ApiResult, +} from './types'; + +export type { + SysOperationLogVO, + TournamentAdminOperationPageVO, + OperationLogQueryParams, + PageData, + ApiResult, +} from './types'; + +/** GET /admin/sys/operation/page — 系统操作日志分页 */ +export async function getOperationLogList( + params: OperationLogQueryParams, +): Promise>> { + return get('/sys/operation/page', params as Record); +} + +/** GET /admin/tournament/operation/page — 赛事操作日志分页 */ +export async function getOperationLogs( + params: OperationLogQueryParams, +): Promise>> { + return get('/tournament/operation/page', params as Record); +} + +/** @deprecated 使用 getOperationLogList */ +export const getOperationLogPage = getOperationLogList; diff --git a/src/api/logs/types.ts b/src/api/logs/types.ts new file mode 100644 index 0000000..cda14a7 --- /dev/null +++ b/src/api/logs/types.ts @@ -0,0 +1,63 @@ +/** + * 操作日志 — 类型定义 + */ + +/** 系统操作日志列表项(GET /admin/sys/operation/page) */ +export interface SysOperationLogVO { + /** 操作类型:1=下架赛事、2=上架赛事、3=禁用用户、4=启用用户 */ + type: number; + /** 操作人昵称 */ + nickname: string; + /** 操作时间 */ + createDate: string; + /** 操作内容 */ + content: string; +} + +/** 赛事操作日志列表项 */ +export interface TournamentAdminOperationPageVO { + /** 操作类型:1=创建赛事、2=删除赛事、3=取消报名、4=编辑选手、5=对阵管理、6=批量修改组别 */ + type: string; + /** 操作来源:1=PC、2=小程序 */ + source: string; + opName: string; + nickname: string; + phone: string; + tournamentName: string; + obj: string; + content: string; + createDate: string; +} + +/** 操作日志列表查询参数 */ +export interface OperationLogQueryParams { + page?: string; + limit?: string; + /** 操作类型 */ + type?: string; + /** 系统日志:操作人昵称 */ + nickname?: string; + /** 赛事日志:操作来源 1=PC,2=小程序 */ + source?: string; + /** 赛事日志:操作人昵称 */ + sourceNickname?: string; + /** 赛事日志:操作人手机号 */ + sourcePhone?: string; + /** 赛事日志:赛事名称 */ + tournamentName?: string; + /** 开始日期 yyyy-MM-dd */ + dateBegin?: string; + /** 结束日期 yyyy-MM-dd */ + dateEnd?: string; +} + +export interface PageData { + total: number; + list: T[]; +} + +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/menu.ts b/src/api/menu.ts index feaac2e..5fb2961 100644 --- a/src/api/menu.ts +++ b/src/api/menu.ts @@ -1,16 +1,132 @@ import { get } from '@/utils/request'; import type { MenuNode, PermissionCode } from '@/types'; +import { FALLBACK_MENU_NODES } from '@/config/fallbackRoutes'; +import { getAuthDataPromise, setAuthDataPromise, clearAuthDataCache } from '@/api/authCache'; +import { forceReLogin } from '@/hooks/useAuth'; +import { useUserStore } from '@/stores/userStore'; + +export { clearAuthDataCache } from '@/api/authCache'; /** - * 获取当前用户的菜单树(后端下发) + * 权限数据(角色 + 权限码) */ -export function fetchMenuTree(): Promise { - return get('/menu/tree'); +export interface AuthData { + roleList: string[]; + permsList: string[]; + nickname: string; + phone: string; +} + +const AUTH_FETCH_FAIL_MSG = '账户权限获取失败,请重新登录'; +const AUTH_EMPTY_MSG = '当前账户权限无任何权限,请联系管理员'; + +function normalizeAuthData(res: any): AuthData { + if (res?.code != null && res.code != 200) { + throw new Error(res.msg || AUTH_FETCH_FAIL_MSG); + } + + const data = res?.data || {}; + const roleList = data.roleList || []; + const permsList = data.permsList || []; + + if (permsList.length === 0) { + throw new Error(AUTH_EMPTY_MSG); + } + + const nickname = data.nickName || ''; + const phone = typeof data.phone === 'string' ? data.phone : ''; + + if (nickname || phone) { + const { setUser } = useUserStore(); + setUser({ + ...(nickname ? { nickname } : {}), + ...(phone ? { phone } : {}), + }); + } + + return { roleList, permsList, nickname, phone }; +} + +function handleAuthFetchFailure(msg?: string) { + clearAuthDataCache(); + forceReLogin(msg); +} + +/** + * 获取当前用户的完整权限数据(角色列表 + 权限编码列表) + * 接口: GET /op/permission + * Promise 级缓存:并发调用共享同一个请求,失败时清除缓存以允许重试 + */ +export function fetchAuthData(): Promise { + const cached = getAuthDataPromise(); + if (cached) return cached; + + const promise = get('/op/permission') + .then((res) => normalizeAuthData(res)) + .catch((err: Error) => { + handleAuthFetchFailure(err.message); + throw err; + }); + + setAuthDataPromise(promise); + return promise; } /** * 获取当前用户的权限编码列表 + * 复用 fetchAuthData 的缓存,避免重复请求 */ export function fetchPermissions(): Promise { - return get('/permission/list'); + return fetchAuthData().then((data) => data.permsList); +} + +// ============================================================ +// 菜单树 — 基于权限过滤兜底路由 +// ============================================================ + +/** + * 按用户拥有的权限码过滤菜单树 + * + * 规则: + * - 节点有 permission 字段 → 权限码必须在 permsSet 中才保留 + * - 节点无 permission 但有 children → 保留并递归过滤子节点 + * - 节点被过滤且父节点过滤后无子节点、无 component → 父节点也移除 + */ +function filterMenuNodesByPermission(nodes: MenuNode[], permsSet: Set): MenuNode[] { + return nodes + .map((node) => { + // 叶子节点:有 permission 则按权限过滤 + if (node.permission) { + if (!permsSet.has(node.permission)) return null; + return { ...node }; + } + + // 分组节点(无 permission 有 children):递归过滤子节点 + if (node.children && node.children.length > 0) { + const filteredChildren = filterMenuNodesByPermission(node.children, permsSet); + // 子节点全部被过滤且父节点无 component → 移除父节点 + if (filteredChildren.length === 0 && !node.component) return null; + return { ...node, children: filteredChildren }; + } + + // 无 permission、无 children、无 component → 保留(如纯展示节点) + return { ...node }; + }) + .filter((node): node is MenuNode => node !== null); +} + +/** + * 获取当前用户的菜单树(基于权限过滤兜底路由) + * + * 1. 调用 /op/permission 获取用户权限码 + * 2. 用权限码过滤 FALLBACK_MENU_NODES + * 3. 返回过滤后的菜单树 + * + * 权限获取失败时由 fetchAuthData 统一强制重新登录 + */ +export function fetchMenuTree(): Promise { + return fetchAuthData().then(({ permsList }) => { + const permsSet = new Set(permsList); + return filterMenuNodesByPermission(FALLBACK_MENU_NODES, permsSet); + }); } diff --git a/src/api/orders/index.ts b/src/api/orders/index.ts new file mode 100644 index 0000000..9bc3953 --- /dev/null +++ b/src/api/orders/index.ts @@ -0,0 +1,69 @@ +/** + * 订单管理 API + * 注意:/admin 前缀已通过 VITE_API_BASE_URL 统一注入(见 .env.*),此文件中只写业务路径。 + */ +import { get, post } from '@/utils/request'; +import type { + OrderListQueryParams, + OrderDetailQueryParams, + OrderPlayerListQueryParams, + RetryRefundParams, + TournamentAdminOrderPageVO, + TournamentAdminOrderInfoVO, + TournamentAdminOrderInfoPageVO, + PageData, + ApiResult, +} from './types'; + +export type { + TournamentAdminOrderPageVO, + TournamentAdminOrderInfoVO, + TournamentAdminOrderInfoPageVO, + InnerRefundInfo, + OrderListQueryParams, + OrderDetailQueryParams, + OrderPlayerListQueryParams, + RetryRefundParams, + PageData, + ApiResult, +} from './types'; + +/** + * 订单管理 - 订单分页 + * GET /admin/manager/order/page → /manager/order/page + */ +export async function getOrderList( + params: OrderListQueryParams, +): Promise>> { + return get('/manager/order/page', params as Record); +} + +/** + * 订单管理 - 订单详情 + * GET /admin/manager/order/info → /manager/order/info + */ +export async function getOrderDetail( + params: OrderDetailQueryParams, +): Promise> { + return get('/manager/order/info', params as Record); +} + +/** + * 订单管理 - 订单详情 - 选手列表 + * GET /admin/manager/order/info/page → /manager/order/info/page + */ +export async function getOrderPlayerList( + params: OrderPlayerListQueryParams, +): Promise>> { + return get('/manager/order/info/page', params as Record); +} + +/** + * 订单管理 - 重新退款 + * POST /admin/manager/order/retryRefund → /manager/order/retryRefund + */ +export function retryRefundOrder( + params: RetryRefundParams, +): Promise>> { + return post('/manager/order/retryRefund', params); +} diff --git a/src/api/orders/types.ts b/src/api/orders/types.ts new file mode 100644 index 0000000..b83bff0 --- /dev/null +++ b/src/api/orders/types.ts @@ -0,0 +1,196 @@ +/** + * 订单管理 — 类型定义 + * 对应 OpenAPI: /admin/manager/order/page + */ + +/** 订单列表项 */ +export interface TournamentAdminOrderPageVO { + /** 订单编号 */ + orderNo: string; + /** 选手ID */ + uniqueId: string; + /** 赛事名称 */ + name: string; + /** 选手名称 */ + realName: string; + /** 手机号 */ + phone: string; + /** 报名比赛组别名称 */ + categoryName: string; + /** 报名人名称 */ + creatorName: string; + /** 报名人手机号 */ + creatorPhone: string; + /** 报名人一共报名人数 */ + creatorSignupCount: number; + /** 订单金额;0.00格式 */ + creatorSignupFee: string; + /** 优惠金额;0.00格式 */ + creatorSignupFeeDiscount: string; + /** 实付金额;0.00格式 */ + creatorSignupFeeActual: string; + /** 退款金额;0.00格式 */ + creatorSignupFeeRefund: string; + /** 订单时间;yyyy.MM.dd HH:mm:ss */ + orderTime: string; + /** 支付时间;yyyy.MM.dd HH:mm:ss */ + payTime: string; + /** 订单状态;1=已支付,2=部分退款,3=全部退款,4=已关闭,-1=其他 */ + orderStatus: string; + /** 退款状态;1=无退款,2=退款成功,3=退款失败,-1=其他 */ + refundStatus: string; +} + +/** 订单列表查询参数 */ +export interface OrderListQueryParams { + page?: string; + limit?: string; + /** 订单时间开始 yyyy-MM-dd */ + orderDate?: string; + /** 订单时间结束 yyyy-MM-dd */ + orderDateEnd?: string; + /** 订单编号 */ + orderNo?: string; + /** 赛事名称 */ + tName?: string; + /** 选手名称 */ + sName?: string; + /** + * 订单状态 + * 不传=全部、1=支付成功,2=部分退款,3=全部退款,4=超时关闭 + */ + status?: string; + /** + * 退款状态 + * 不传=全部、2=退款成功,3=退款失败 + */ + refundStatus?: string; +} + +/** 订单分页数据(含列表统计字段) */ +export interface OrderPageData extends PageData { + /** 扩展数据 */ + exData: { + /** 总订单金额(已支付) */ + totalOrderAmount: string; + /** 总实付金额 */ + totalActualAmount: string; + /** 总退款金额 */ + totalRefundAmount: string; + }; +} + +/** 分页数据 */ +export interface PageData { + total: number; + list: T[]; +} + +/** 统一响应包裹 */ +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} + +// ============================================================ +// 订单详情(对应 /manager/order/info) +// ============================================================ + +/** 退款记录 */ +export interface InnerRefundInfo { + /** 退款金额;0.00格式 */ + refundAmount: string; + /** 退款方式 */ + refundMethod: string; + /** 退款申请时间 */ + refundApplyTime: string; + /** 第三方退款单号 */ + refundOutTradeNo: string; + /** 退款完成时间 */ + refundTime: string; + /** 退款状态;1=退款中,2=退款成功,3=退款失败 */ + refundStatus: string; + /** 退款选手名称合集文字 */ + refundSignupNames: string; +} + +/** 订单详情 */ +export interface TournamentAdminOrderInfoVO { + /** 赛事名称 */ + tournamentName: string; + /** 订单编号 */ + orderNumber: string; + /** 订单时间 */ + orderTime: string; + /** 支付时间 */ + payTime: string; + /** 订单金额;0.00格式 */ + orderAmount: string; + /** 优惠金额;0.00格式 */ + discountAmount: string; + /** 实付金额;0.00格式 */ + payAmount: string; + /** 退款金额;0.00格式 */ + refundAmount: string; + /** 退款信息list */ + refundInfoList: InnerRefundInfo[]; +} + +/** 订单详情查询参数 */ +export interface OrderDetailQueryParams { + /** 订单编号 */ + orderNo?: string; + /** 选手ID */ + uniqueId?: string; +} + +// ============================================================ +// 订单详情 — 选手列表(对应 /manager/order/info/page) +// ============================================================ + +/** 订单下选手信息 */ +export interface TournamentAdminOrderInfoPageVO { + /** 姓名 */ + realName: string; + /** 性别;1=男,2=女(接口实际返回 string) */ + gender: string; + /** 手机号 */ + phone: string; + /** 证件号 */ + idCard: string; + /** 证件号图片 */ + idCardImg: string; + /** 选手身份证图片列表 */ + idCardImgList: string[]; + /** 选手组别名称 */ + categoryName: string; + /** 订单金额;0.00 */ + orderAmount: string; + /** 优惠金额;0.00 */ + discountAmount: string; + /** 实付金额;0.00 */ + payAmount: string; + /** 退款金额;0.00 */ + refundAmount: string; +} + +/** 选手列表查询参数 */ +export interface OrderPlayerListQueryParams { + /** 订单编号 */ + orderNo?: string; + /** 选手ID */ + uniqueId?: string; + page: string; + limit: string; +} + +// ============================================================ +// 重新退款(对应 /manager/order/retryRefund) +// ============================================================ + +/** 重新退款请求参数 */ +export interface RetryRefundParams { + /** 支付单号 */ + payOrderNo: string; +} diff --git a/src/api/params/index.ts b/src/api/params/index.ts new file mode 100644 index 0000000..593e017 --- /dev/null +++ b/src/api/params/index.ts @@ -0,0 +1,49 @@ +/** + * 参数配置 API + * + * TODO: 等后端接口确定后,替换占位的 URL 和返回类型 + */ +import { get, post, put, del } from '@/utils/request'; +import type { ParamQueryParams, SysParamVO, PageData, ApiResult } from './types'; + +// 类型重导出,方便页面 model 层统一引用 +export type { SysParamVO, ParamQueryParams, PageData, ApiResult } from './types'; + +// ============================================================ +// URL 常量(占位,等后端接口确定后替换) +// ============================================================ + +/** 配置列表分页查询 */ +const PARAM_LIST = '/admin/sys/param/page'; +/** 新增配置 */ +const PARAM_ADD = '/admin/sys/param/add'; +/** 编辑配置 */ +const PARAM_UPDATE = '/admin/sys/param/update'; +/** 删除配置 */ +const PARAM_DELETE = '/admin/sys/param/delete'; + +// ============================================================ +// API 函数(占位) +// ============================================================ + +/** 配置列表分页 */ +export async function getParamList( + params: ParamQueryParams, +): Promise>> { + return get(PARAM_LIST, params as Record); +} + +/** 新增配置 */ +export async function addParam(data: Partial): Promise> { + return post(PARAM_ADD, data); +} + +/** 编辑配置 */ +export async function updateParam(data: Partial): Promise> { + return put(PARAM_UPDATE, data); +} + +/** 删除配置 */ +export async function deleteParam(id: number): Promise> { + return del(`${PARAM_DELETE}`, { id }); +} diff --git a/src/api/params/types.ts b/src/api/params/types.ts new file mode 100644 index 0000000..5a6c40c --- /dev/null +++ b/src/api/params/types.ts @@ -0,0 +1,40 @@ +/** + * 参数配置 — 类型定义 + * + * TODO: 等后端接口确定后,根据实际字段补充完整类型 + */ + +/** 配置项列表项 */ +export interface SysParamVO { + /** 配置项 ID */ + id: number; + /** 配置项 key */ + // paramKey: string; + /** 配置项 value */ + // paramValue: string; + /** 备注 */ + // remark: string; + /** 更新时间 */ + // updateTime: string; +} + +/** 配置列表查询参数 */ +export interface ParamQueryParams { + page?: string; + limit?: string; + /** 搜索关键字 */ + // keyword?: string; +} + +/** 分页数据结构 */ +export interface PageData { + total: number; + list: T[]; +} + +/** API 统一响应格式 */ +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/payments/index.ts b/src/api/payments/index.ts new file mode 100644 index 0000000..ba0c61d --- /dev/null +++ b/src/api/payments/index.ts @@ -0,0 +1,26 @@ +/** + * 支付流水 API + * 对应 OpenAPI: GET /admin/finance/paymentFlow/page + */ +import { get } from '@/utils/request'; +import type { + PaymentFlowQueryParams, + PaymentFlowVO, + PaymentFlowPageData, + ApiResult, +} from './types'; + +export type { + PaymentFlowVO, + PaymentFlowQueryParams, + PaymentFlowPageData, + PaymentFlowExData, + ApiResult, +} from './types'; + +/** GET /admin/finance/paymentFlow/page — 支付流水分页 */ +export async function getPaymentFlowList( + params: PaymentFlowQueryParams, +): Promise> { + return get('/finance/paymentFlow/page', params as Record); +} diff --git a/src/api/payments/types.ts b/src/api/payments/types.ts new file mode 100644 index 0000000..a946cec --- /dev/null +++ b/src/api/payments/types.ts @@ -0,0 +1,64 @@ +/** + * 支付流水 — 类型定义 + * 对应 OpenAPI: GET /admin/finance/paymentFlow/page + */ + +/** 支付流水列表项 */ +export interface PaymentFlowVO { + /** 流水号 */ + serialNo: string; + /** 订单编号 */ + orderNo: string; + /** 类型:1=报名,2=取消报名 */ + type: number; + /** 用户昵称 */ + nickname: string; + /** 用户手机�� */ + phone: string; + /** 订单金额("0.00"格式) */ + amount: string; + /** 订单创建时间 */ + createDate: string; +} + +/** 支付流水查询参数 */ +export interface PaymentFlowQueryParams { + page?: string; + limit?: string; + /** 订单创建开始时间 yyyy-MM-dd */ + createDateBegin?: string; + /** 订单创建结束时间 yyyy-MM-dd */ + createDateEnd?: string; + /** 用户昵称 */ + nickname?: string; + /** 手机号 */ + phone?: string; + /** 类型:1/2 */ + type?: string; +} + +export interface PageData { + total: number; + list: T[]; +} + +/** 支付流水分页扩展汇总(与 total 同级) */ +export interface PaymentFlowExData { + /** 总支付金额 */ + totalAmount: string; + /** 总退款金额 */ + totalRefundAmount: string; +} + +/** 支付流水分页响应 */ +export interface PaymentFlowPageData { + total: number; + list: PaymentFlowVO[]; + exData: PaymentFlowExData; +} + +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/reports/index.ts b/src/api/reports/index.ts new file mode 100644 index 0000000..16f5415 --- /dev/null +++ b/src/api/reports/index.ts @@ -0,0 +1,13 @@ +/** + * 财务报表 API + * 对应 OpenAPI: GET /admin/finance/report/list + */ +import { get } from '@/utils/request'; +import type { ReportQueryParams, FinanceReportVO, ApiResult } from './types'; + +export type { ReportQueryParams, FinanceReportVO, ReportMonthItem, ApiResult } from './types'; + +/** GET /admin/finance/report/list — 财务报表 */ +export function getFinanceReport(params: ReportQueryParams): Promise> { + return get('/admin/finance/report/list', params as Record); +} diff --git a/src/api/reports/types.ts b/src/api/reports/types.ts new file mode 100644 index 0000000..d82a477 --- /dev/null +++ b/src/api/reports/types.ts @@ -0,0 +1,48 @@ +/** + * 财务报表 — 类型定义 + * 对应 OpenAPI: GET /admin/finance/report/list + */ + +/** 月度明细项(对应 InnerMonthVO) */ +export interface ReportMonthItem { + /** 月份 yyyy-MM */ + month: string; + /** 订单金额,0.00 格式 */ + orderAmount: string; + /** 退款金额,0.00 格式 */ + refundAmount: string; + /** 净收入 = orderAmount - refundAmount,0.00 格式 */ + netIncome: string; + /** 提现金额,0.00 格式 */ + withdrawAmount: string; +} + +/** 财务报表汇总(对应 TournamentFinanceReportVO) */ +export interface FinanceReportVO { + /** 总收入(扣除退款后),0.00 格式 */ + totalIncome: string; + /** 总收入较上月增长率 */ + totalIncomeTendency: string; + /** 总提现金额,0.00 格式 */ + totalWithdrawIncome: string; + /** 总提现金额较上月增长率 */ + totalWithdrawIncomeTendency: string; + /** 平台余额 = totalIncome - totalWithdrawIncome,0.00 格式 */ + totalBalanceAmount: string; + /** 月度收支明细 */ + monthList: ReportMonthItem[]; +} + +/** 财务报表查询参数 */ +export interface ReportQueryParams { + /** 起始月份 yyyy-MM */ + beginDate: string; + /** 结束月份 yyyy-MM */ + endDate: string; +} + +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/roles/index.ts b/src/api/roles/index.ts new file mode 100644 index 0000000..cf092e5 --- /dev/null +++ b/src/api/roles/index.ts @@ -0,0 +1,49 @@ +/** + * 角色管理 API + */ +import { get, post } from '@/utils/request'; +import type { + RoleListQueryParams, + RoleUsageUserQueryParams, + TournamentAdminRolePageVO, + RoleUsageUserVO, + PermissionTreeNode, + PageData, + ApiResult, +} from './types'; + +export type { + TournamentAdminRolePageVO, + RoleUsageUserVO, + RoleListQueryParams, + RoleUsageUserQueryParams, + PermissionTreeNode, + PageData, + ApiResult, +} from './types'; + +/** GET /sys/role/page — 角色列表分页 */ +export async function getRoleList( + params: RoleListQueryParams, +): Promise>> { + return get('/admin/sys/role/page', params as Record); +} + +/** GET /sys/role/usageUserPage — 角色下用户列表分页 */ +export async function getRoleUsageUserList( + params: RoleUsageUserQueryParams, +): Promise>> { + return get('/sys/role/usageUserPage', params as Record); +} + +/** GET /admin/sys/role/permissionList?roleId=xxx — 角色权限树 */ +export async function getPermissionTree( + roleId: number | string, +): Promise> { + return get('/sys/role/permissionList', { roleId: String(roleId) }); +} + +/** POST /admin/sys/role/del — 删除角色 */ +export async function deleteRole(id: number): Promise> { + return post('/sys/role/del', { id }); +} diff --git a/src/api/roles/types.ts b/src/api/roles/types.ts new file mode 100644 index 0000000..fc9b4d1 --- /dev/null +++ b/src/api/roles/types.ts @@ -0,0 +1,79 @@ +/** + * 角色管理 — 类型定义 + * 对应 OpenAPI: /admin/sys/role/* + */ + +/** 角色列表项 */ +export interface TournamentAdminRolePageVO { + /** 角色ID */ + roleId: number; + /** 角色名称 */ + roleName: string; + /** 角色用户数量 */ + userCount: number; + /** 角色创建时间 */ + createTime: string; +} + +/** 角色列表查询参数 */ +export interface RoleListQueryParams { + page?: string; + limit?: string; + /** 角色名称(模糊搜索) */ + name?: string; +} + +/** 角色下用户列表项 */ +export interface RoleUsageUserVO { + realName: string; + phone: string; + createTime: string; + /** 状态: 0=停用、1=正常 */ + status: number; +} + +/** 角色下用户列表查询参数 */ +export interface RoleUsageUserQueryParams { + page?: string; + limit?: string; + /** 用户姓名或手机号 */ + text?: string; + roleId?: string; +} + +/** 保存角色 — POST /admin/sys/role/save */ +export interface RoleSaveParams { + name: string; + menuIds: string[]; +} + +/** 更新角色 — POST /admin/sys/role/update */ +export interface RoleUpdateParams { + id: string; + name: string; + menuIds: string[]; +} + +export interface PageData { + total: number; + list: T[]; +} +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} + +/** 权限树节点(后端 /admin/sys/role/permissionList 返回) */ +export interface PermissionTreeNode { + /** 主键 */ + id: number; + /** 上级ID */ + pid: number; + /** 标题 */ + name: string; + /** 是否选中:true=是,false=否,null=空(仅叶节点有意义,父节点遵循级联逻辑) */ + selected: boolean | null; + /** 子节点 */ + children?: PermissionTreeNode[]; +} diff --git a/src/api/upload.ts b/src/api/upload.ts new file mode 100644 index 0000000..e732da7 --- /dev/null +++ b/src/api/upload.ts @@ -0,0 +1,27 @@ +import { get } from '@/utils/request'; +import type { OssUploadTypeValue } from '@/utils/oss'; + +/** OSS 签名数据 */ +export interface OssSignData { + host: string; + dir: string; + policy: string; + signature: string; + x_oss_credential: string; + x_oss_date: string; + security_token: string; +} + +/** + * 获取 OSS 上传签名 + * @param type 上传类型(参见 OssUploadType) + */ +export async function fetchOssSign(type: OssUploadTypeValue): Promise { + // 后端签名接口地址(待对接时调整) + const res: any = await get('/sys/oss/getUploadSign', { type }); + const data = res?.data as OssSignData; + if (!data?.host) { + throw new Error(res?.msg || '获取上传凭证失败'); + } + return data; +} diff --git a/src/api/users/index.ts b/src/api/users/index.ts new file mode 100644 index 0000000..73efc60 --- /dev/null +++ b/src/api/users/index.ts @@ -0,0 +1,56 @@ +/** + * 用户管理 API + */ +import { get, post } from '@/utils/request'; +import type { + UserListQueryParams, + UserSaveParams, + UserUpdateParams, + UserActiveParams, + UserUpdatePwdParams, + TournamentAdminUserVO, + PageData, + ApiResult, +} from './types'; + +export type { + TournamentAdminUserVO, + UserListQueryParams, + UserSaveParams, + UserUpdateParams, + UserActiveParams, + UserUpdatePwdParams, + PageData, + ApiResult, +} from './types'; + +/** GET /admin/sys/user/page — 用户分页 */ +export async function getUserList( + params: UserListQueryParams, +): Promise>> { + return get('/admin/sys/user/page', params as Record); +} + +/** POST /admin/sys/user/save — 新增用户 */ +export function saveUser(params: UserSaveParams): Promise>> { + return post('/admin/sys/user/save', params); +} + +/** POST /admin/sys/user/update — 更新用户 */ +export function updateUser(params: UserUpdateParams): Promise>> { + return post('/admin/sys/user/update', params); +} + +/** POST /sys/user/active — 启用/禁用(直接调接口) */ +export function toggleUserActive( + params: UserActiveParams, +): Promise>> { + return post('/admin/sys/user/active', params); +} + +/** POST /sys/user/updatepwd — 修改密码(直接调接口) */ +export function updateUserPwd( + params: UserUpdatePwdParams, +): Promise>> { + return post('/admin/sys/user/updatepwd', params); +} diff --git a/src/api/users/types.ts b/src/api/users/types.ts new file mode 100644 index 0000000..1c8eb6c --- /dev/null +++ b/src/api/users/types.ts @@ -0,0 +1,71 @@ +/** + * 用户管理 — 类型定义 + * 对应 OpenAPI: /admin/sys/user/* + */ + +export interface TournamentAdminUserVO { + id: string; + /** 用户姓名 */ + realName: string; + phone: string; + /** 角色名称 */ + roleName: string; + /** 角色ID(编辑时需要回显,列表可能不返回) */ + roleId?: string; + /** 状态;0=停用、1=正常(接口实际返回 string) */ + status: string; + createTime: string; +} + +export interface UserListQueryParams { + page?: string; + limit?: string; + /** 用户姓名或手机号 */ + text?: string; + /** 角色ID */ + roleId?: string; + /** 状态;不传=全部、0=停用、1=正常 */ + status?: string; +} + +/** 新增用户 — POST /admin/sys/user/save */ +export interface UserSaveParams { + realName: string; + phone: string; + roleId: string; + password: string; + confirmPassword: string; +} + +/** 更新用户 — POST /admin/sys/user/update */ +export interface UserUpdateParams { + id: string; + realName: string; + phone: string; + roleId: string; + password?: string; + confirmPassword?: string; +} + +/** 启用/禁用 — POST /admin/sys/user/active */ +export interface UserActiveParams { + userId: string; + /** 0=禁用、1=启用 */ + status: string; +} + +/** 修改密码 — POST /admin/sys/user/updatepwd */ +export interface UserUpdatePwdParams { + password: string; + confirmPassword: string; +} + +export interface PageData { + total: number; + list: T[]; +} +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} diff --git a/src/api/wallet/index.ts b/src/api/wallet/index.ts new file mode 100644 index 0000000..9b323b3 --- /dev/null +++ b/src/api/wallet/index.ts @@ -0,0 +1,38 @@ +/** + * 用户钱包 API + * OpenAPI: GET /admin/finance/wallet/summary, GET /admin/finance/wallet/page + */ +import { get } from '@/utils/request'; +import type { + WalletQueryParams, + WalletSummaryVO, + WalletSummaryPageData, + WalletTransactionQueryParams, + WalletTransactionVO, + PageData, + ApiResult, +} from './types'; + +export type { + WalletSummaryVO, + WalletSummaryPageData, + WalletQueryParams, + WalletTransactionVO, + WalletTransactionQueryParams, + PageData, + ApiResult, +} from './types'; + +/** GET /admin/finance/wallet/summary — 钱包列表分页 */ +export async function getWalletList( + params: WalletQueryParams, +): Promise> { + return get('/finance/wallet/summary', params as Record); +} + +/** GET /admin/finance/wallet/page — 交易明细分页 */ +export async function getWalletTransactionList( + params: WalletTransactionQueryParams, +): Promise>> { + return get('/finance/wallet/page', params as Record); +} diff --git a/src/api/wallet/types.ts b/src/api/wallet/types.ts new file mode 100644 index 0000000..7dba886 --- /dev/null +++ b/src/api/wallet/types.ts @@ -0,0 +1,105 @@ +/** + * 用户钱包 — 类型定义 + * 对应 OpenAPI: GET /admin/finance/wallet/summary + */ + +/** 钱包列表项 */ +export interface WalletSummaryVO { + /** 用户ID */ + userId: number; + /** 用户昵称 */ + userNickname: string; + /** 手机号 */ + phone: string; + /** 余额("0.00"格式) */ + amount: string; + /** 冻结金额("0.00"格式) */ + frozenAmount: string; + /** 累计成功提现金额("0.00"格式) */ + withdrawalAmount: string; +} + +/** 钱包汇总扩展数据(与 total 同级) */ +export interface WalletSummaryExData { + /** 总余额 */ + totalAmount: string; + /** 总冻结金额 */ + totalFrozenAmount: string; + /** 总累计提现 */ + totalWithdrawalAmount: string; +} + +/** 钱包列表分页响应 */ +export interface WalletSummaryPageData { + total: number; + list: WalletSummaryVO[]; + exData: WalletSummaryExData; +} + +/** 钱包列表查询参数 */ +export interface WalletQueryParams { + page?: string; + limit?: string; + /** 用户昵称,模糊搜索 */ + nickname?: string; + /** 手机号 */ + phone?: string; + /** 余额最小值 */ + amountBegin?: string; + /** 余额最大值 */ + amountEnd?: string; +} + +export interface PageData { + total: number; + list: T[]; +} +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} + +// ============================================================ +// 交易明细(GET /admin/finance/wallet/page) +// ============================================================ + +/** 交易明细列表项 */ +export interface WalletTransactionVO { + /** 类型:1=报名入账,2=提现出账,3=退款出账 */ + type: number; + /** 金额("0.00"格式,入账为+,出账为-) */ + amount: string; + /** 余额("0.00"格式) */ + afterAmount: string; + /** 可提现余额("0.00"格式) */ + withdrawAmount: string; + /** 冻结金额("0.00"格式) */ + freezeAmount: string; + /** 冻结时间 */ + freezeTime: string; + /** 解冻时间 */ + unfreezeTime: string; + /** 关联单号 */ + orderNo: string; + /** 时间 */ + createDate: string; + /** 状态;1=已解冻,2=冻结中,3=正常 */ + status: number; +} + +/** 交易明细查询参数 */ +export interface WalletTransactionQueryParams { + page?: string; + limit?: string; + /** 用户ID */ + userId?: string; + /** 创建开始时间 yyyy-MM-dd */ + createDateBegin?: string; + /** 创建结束时间 yyyy-MM-dd */ + createDateEnd?: string; + /** 类型:1/2/3 */ + type?: string; + /** 状态:1=已解冻,2=冻结中,3=正常 */ + status?: string; +} diff --git a/src/api/withdraw/index.ts b/src/api/withdraw/index.ts new file mode 100644 index 0000000..13f7d54 --- /dev/null +++ b/src/api/withdraw/index.ts @@ -0,0 +1,37 @@ +/** + * 提现管理 API + * 对应 OpenAPI: GET /admin/finance/withdraw/page, GET /admin/finance/withdraw/info + */ +import { get, post } from '@/utils/request'; +import type { WithdrawQueryParams, WithdrawVO, WithdrawInfoVO, PageData, ApiResult } from './types'; + +export type { WithdrawVO, WithdrawInfoVO, WithdrawQueryParams, PageData, ApiResult } from './types'; + +/** GET /admin/finance/withdraw/page — 提现列表分页 */ +export async function getWithdrawList( + params: WithdrawQueryParams, +): Promise>> { + return get('/admin/finance/withdraw/page', params as Record); +} + +/** GET /admin/finance/withdraw/info?id=xxx — 提现详情 */ +export async function getWithdrawInfo(id: number): Promise> { + return get('/admin/finance/withdraw/info', { id: String(id) }); +} + +/** 提现审核请求参数 */ +export interface WithdrawAuditParams { + /** 提现ID */ + id: string; + /** 审核结果:1=通过,2=驳回 */ + audit: string; + /** 审核内容 */ + checkMsg: string; + /** 审核图片(逗号拼接的 URL) */ + checkImg: string; +} + +/** POST /admin/finance/withdraw/audit — 审核提现 */ +export async function postWithdrawAudit(params: WithdrawAuditParams): Promise> { + return post('/admin/finance/withdraw/audit', params); +} diff --git a/src/api/withdraw/types.ts b/src/api/withdraw/types.ts new file mode 100644 index 0000000..74c432e --- /dev/null +++ b/src/api/withdraw/types.ts @@ -0,0 +1,106 @@ +/** + * 提现管理 — 类型定义 + * 对应 OpenAPI: GET /admin/finance/withdraw/page + */ + +/** 提现列表项(对齐 TournamentFinanceWithdrawVO) */ +export interface WithdrawVO { + /** 提现ID */ + id: number; + /** 申请人昵称 */ + nickname: string; + /** 申请人手机号 */ + phone: string; + /** 申请人真实姓名 */ + realName: string; + /** 提现类型(固定"银行卡") */ + withdrawType: string; + /** 提现金额("0.00"格式) */ + withdrawAmount: string; + /** 到账金额("0.00"格式) */ + receivedAmount: string; + /** 审核状态:0=待审核,1=审核通过,2=审核驳回 */ + auditStatus: number; + /** 到账状态:0=未支付,1=支付成功,2=支付失败 */ + payStatus: number; + /** 第三方单号 */ + serialNumber: string; + /** 申请时间 */ + createDate: string; +} + +/** 提现列表查询参数 */ +export interface WithdrawQueryParams { + page?: string; + limit?: string; + /** 提现申请人昵称 */ + nickname?: string; + /** 提现申请人手机号 */ + phone?: string; + /** 提现申请人真实姓名 */ + realName?: string; + /** 审核状态:0/1/2 */ + auditStatus?: string; + /** 到账状态:0/1/2 */ + payStatus?: string; +} + +export interface PageData { + total: number; + list: T[]; +} +export interface ApiResult { + code: number | string; + msg: string; + data: T; +} + +/** 提现详情(对齐 TournamentFinanceWithdrawInfoVO) */ +export interface WithdrawInfoVO { + /** 申请人昵称 */ + nickname: string; + /** 申请人手机号 */ + phone: string; + /** 申请人真实姓名 */ + realName: string; + /** 申请时间 */ + createDate: string; + /** 提现类型(固定"银行卡") */ + withdrawType: string; + /** 提现金额("0.00"格式) */ + withdrawAmount: string; + /** 费率 */ + feeRate: string; + /** 手续费 */ + feeAmount: string; + /** 到账金额("0.00"格式) */ + receivedAmount: string; + /** 审核状态:0=待审核,1=审核通过,2=审核驳回 */ + auditStatus: number; + /** 到账状态:0=未支付,1=支付成功,2=支付失败 */ + payStatus: number; + /** 银行卡类型:1=个人 */ + bankCardType: number; + /** 持卡人真实姓名 */ + cardHolderName: string; + /** 银行卡号 */ + bankCardNumber: string; + /** 开户行名称 */ + bankName: string; + /** 支行名称 */ + branchName: string; + /** 支付时间 */ + payTime: string; + /** 订单号 */ + orderNo: string; + /** 审核时间 */ + auditDate: string; + /** 审核人昵称 */ + auditNickname: string; + /** 审核图片 */ + auditImg: string; + /** 审核图片列表 */ + auditImgList: string[]; + /** 审核结果 */ + checkMsg: string; +} diff --git a/src/assets/img/default.png b/src/assets/img/default.png new file mode 100644 index 0000000..d8b4f5b Binary files /dev/null and b/src/assets/img/default.png differ diff --git a/src/assets/img/icon/logout.svg b/src/assets/img/icon/logout.svg new file mode 100644 index 0000000..43a1425 --- /dev/null +++ b/src/assets/img/icon/logout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/styles/index.less b/src/assets/styles/index.less index b5a78cc..e5c92db 100644 --- a/src/assets/styles/index.less +++ b/src/assets/styles/index.less @@ -55,3 +55,91 @@ a { ::-webkit-scrollbar-track { background: transparent; } + +// ===== 页面布局公共类 ===== + +// 页面容器:纵向 flex,占满高度;筛选区与表格区作为直接子元素 +.page-container { + display: flex; + flex-direction: column; + height: 100%; +} + +// 页面筛选区域:横向 flex,可换行 +.page-filter { + display: flex; + flex-wrap: wrap; + align-items: center; + column-gap: 16px; + row-gap: 12px; + flex-shrink: 0; + padding: 16px; + background-color: #fff; + border-radius: 10px; +} + +// 筛选区域操作按钮组:横向 flex +.page-filter-actions { + display: flex; + align-items: center; + gap: 10px; + margin-left: 0; +} + +// 表格显示区域:纵向 flex,占据剩余高度。 +// overflow: hidden 防止子元素撑开容器,滚动由 Table 的 scroll.x/y 接管。 +// 分页组件作为独立子元素置于 Table 下方,不占用 Table 的 scroll 空间。 +.page-table { + display: flex; + flex-direction: column; + flex: 1; + background-color: #fff; + min-height: 0; + min-width: 0; + margin-top: 16px; + overflow: hidden; + border-radius: 10px; +} + +.page-pagination { + flex-shrink: 0; + display: flex; + justify-content: flex-end; + padding: 14px; +} + +// ===== antd 组件覆盖 ===== + +// Tag 默认 margin-right: 8px,会挤开表格列或卡片内间距,统一去掉 +.ant-tag { + margin-right: 0; +} + +// ===== 页面切换动画(KeepAlive + RouterView) ===== + +.page-fade-slide-enter-active, +.page-fade-slide-leave-active { + width: 100%; + height: 100%; + min-height: 0; + transition: + opacity 0.24s cubic-bezier(0.4, 0, 0.2, 1), + transform 0.24s cubic-bezier(0.4, 0, 0.2, 1); +} + +.page-fade-slide-enter-from { + opacity: 0; + transform: translateX(12px); +} + +.page-fade-slide-leave-to { + opacity: 0; + transform: translateX(-12px); +} + +.page-fade-slide-leave-active { + position: absolute; + inset: 0; + width: 100%; + overflow: hidden; +} diff --git a/src/assets/styles/pageLayout.module.less b/src/assets/styles/pageLayout.module.less new file mode 100644 index 0000000..b5ba3fb --- /dev/null +++ b/src/assets/styles/pageLayout.module.less @@ -0,0 +1,125 @@ +// 页面布局根容器:筛选区 + 表格区作为直接子元素 +.containerMain { + display: flex; + flex-direction: column; + height: 100%; + + // 页面筛选区域:横向 flex,可换行 + .filter { + display: flex; + flex-wrap: wrap; + align-items: center; + column-gap: 16px; + row-gap: 12px; + flex-shrink: 0; + padding: 16px; + background-color: #fff; + border-radius: 10px; + } + + // 筛选区域操作按钮组 + .filterActions { + display: flex; + align-items: center; + gap: 10px; + margin-left: 0; + } + + // 表格显示区域:纵向 flex,占据剩余高度 + .table { + display: flex; + flex-direction: column; + flex: 1; + background-color: #fff; + min-height: 0; + min-width: 0; + margin-top: 16px; + overflow: hidden; + border-radius: 10px; + + .tableBody { + flex: 1; + min-height: 0; + overflow: hidden; + + :global { + .ant-spin-nested-loading, + .ant-spin-container, + .ant-table, + .ant-table-container { + height: 100%; + } + } + } + + // 表格内可点击数字链接(等宽数字,避免列宽抖动) + .tabularLink { + font-variant-numeric: tabular-nums; + cursor: pointer; + } + + // ── 汇总 ── + .summary { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0 32px; + flex-shrink: 0; + padding: 14px 15px; + border-radius: 8px; + font-size: 14px; + color: rgba(0, 0, 0, 0.85); + } + + .summaryItem { + display: inline-flex; + align-items: baseline; + } + + .summaryLabel { + color: rgba(0, 0, 0, 0.65); + } + + .summaryValue { + font-weight: 600; + color: rgba(0, 0, 0, 0.85); + letter-spacing: 0.3px; + } + + .pagination { + flex-shrink: 0; + display: flex; + justify-content: flex-end; + padding: 14px; + } + } +} + +// ===== 二次确认弹窗 ===== + +.confirmContent { + padding: 4px 0; + font-size: 14px; + line-height: 1.6; + color: rgba(0, 0, 0, 0.85); + + strong { + margin: 0 4px; + font-weight: 600; + color: rgba(0, 0, 0, 0.88); + } + + .confirmTip { + margin-top: 8px; + font-size: 12px; + line-height: 1.5; + color: rgba(0, 0, 0, 0.45); + } +} + +:global(.toggleConfirmModal), +:global(.deleteConfirmModal) { + .ant-modal-confirm-body { + padding: 24px !important; + } +} diff --git a/src/components/ChangePasswordModal/controller.ts b/src/components/ChangePasswordModal/controller.ts new file mode 100644 index 0000000..22ccb67 --- /dev/null +++ b/src/components/ChangePasswordModal/controller.ts @@ -0,0 +1,48 @@ +/** + * 修改密码弹窗 - 表单域控制器 + * + * 集中管理弹窗内的所有规则与提示文案。 + */ +import type { Rule } from 'ant-design-vue/es/form'; +import { isValidPassword } from '@/utils/form'; + +// ============================================================ +// 文案 +// ============================================================ + +/** 弹窗底部提示 */ +export const TIPS_TEXT = '修改后立即生效,请妥善保管新密码'; + +// ============================================================ +// 表单规则 +// ============================================================ + +/** 新密码规则:必填 + 至少 8 位 + 字母 + 数字 */ +export const newPasswordRules: Rule[] = [ + { required: true, message: '请输入新密码' }, + { + validator: (_rule: unknown, value: string) => { + if (!value) return Promise.resolve(); + if (!isValidPassword(value)) { + return Promise.reject(new Error('密码至少8位,需包含字母与数字')); + } + return Promise.resolve(); + }, + }, +]; + +/** 确认密码规则:必填 + 必须与新密码一致 */ +export function confirmNewPasswordRules(getPassword: () => string): Rule[] { + return [ + { required: true, message: '请再次输入新密码' }, + { + validator: (_rule: unknown, value: string) => { + if (!value) return Promise.resolve(); + if (value !== getPassword()) { + return Promise.reject(new Error('两次输入的密码不一致')); + } + return Promise.resolve(); + }, + }, + ]; +} diff --git a/src/components/ChangePasswordModal/index.tsx b/src/components/ChangePasswordModal/index.tsx new file mode 100644 index 0000000..faef592 --- /dev/null +++ b/src/components/ChangePasswordModal/index.tsx @@ -0,0 +1,107 @@ +import { defineComponent, ref, reactive, nextTick } from 'vue'; +import { useEffect } from '@/hooks'; +import { Modal, Form, Input, Button, message } from 'ant-design-vue'; +import { TIPS_TEXT, newPasswordRules, confirmNewPasswordRules } from './controller'; +import styles from './style.module.less'; + +interface ChangePasswordModalProps { + visible: boolean; + submitting?: boolean; + onClose: () => void; + /** 提交回调:父组件负责调用 API,loading 状态由 submitting 控制 */ + onSave: (payload: { newPassword: string }) => void; +} + +/** 默认表单数据 */ +const getDefaultForm = () => ({ + newPassword: '', + confirmPassword: '', +}); + +export default defineComponent({ + name: 'ChangePasswordModal', + props: { + visible: { type: Boolean, default: false }, + submitting: { type: Boolean, default: false }, + onClose: { type: Function, required: true }, + onSave: { type: Function, required: true }, + }, + setup(props: ChangePasswordModalProps) { + const formRef = ref(); + const formData = reactive(getDefaultForm()); + + /** 监听 visible 重置表单 */ + useEffect(() => { + if (props.visible) { + Object.assign(formData, getDefaultForm()); + nextTick().then(() => formRef.value?.clearValidate()); + } + }, [() => props.visible]); + + /** 提交 */ + const handleSubmit = async () => { + try { + await formRef.value?.validate(); + } catch { + return; + } + props.onSave({ newPassword: formData.newPassword }); + }; + + const handleClose = () => { + props.onClose(); + }; + + return () => ( + +
+ + { + formData.newPassword = val; + }} + placeholder="至少8位,含字母+数字" + allowClear + /> + + + formData.newPassword)} + > + { + formData.confirmPassword = val; + }} + placeholder="再次输入新密码" + allowClear + /> + + +
{TIPS_TEXT}
+
+ + {/* 底部按钮 */} +
+ + +
+
+ ); + }, +}); diff --git a/src/components/ChangePasswordModal/style.module.less b/src/components/ChangePasswordModal/style.module.less new file mode 100644 index 0000000..4606684 --- /dev/null +++ b/src/components/ChangePasswordModal/style.module.less @@ -0,0 +1,36 @@ +.changePasswordModalMain { + .modal { + :global { + .ant-modal-body { + padding: 16px 24px 0; + } + } + } + + .form { + :global { + .ant-form-item { + margin-bottom: 16px; + } + .ant-form-item-explain-error { + font-size: 12px; + } + } + } + + .tip { + margin-top: -4px; + margin-bottom: 24px; + color: rgba(0, 0, 0, 0.45); + font-size: 12px; + line-height: 1.5; + } + + .footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding-top: 16px; + border-top: 1px solid #f0f0f0; + } +} diff --git a/src/components/HelloWorld.module.less b/src/components/HelloWorld.module.less index eaab200..32d823a 100644 --- a/src/components/HelloWorld.module.less +++ b/src/components/HelloWorld.module.less @@ -1,16 +1,16 @@ -.container { +.helloWorldMain { text-align: center; padding: 16px; -} -.title { - color: #1677ff; -} + .title { + color: #1677ff; + } -.desc { - code { - background: #f5f5f5; - padding: 2px 6px; - border-radius: 4px; + .desc { + code { + background: #f5f5f5; + padding: 2px 6px; + border-radius: 4px; + } } } diff --git a/src/components/HelloWorld.tsx b/src/components/HelloWorld.tsx index 1b59629..b7fd3bb 100644 --- a/src/components/HelloWorld.tsx +++ b/src/components/HelloWorld.tsx @@ -14,7 +14,7 @@ export default defineComponent({ }, setup(props) { return () => ( -
+

{props.msg}

这是一个公共组件示例,采用 TSX 写法。你可以在 diff --git a/src/components/StatusTag/StatusTag.module.less b/src/components/StatusTag/StatusTag.module.less new file mode 100644 index 0000000..57156b2 --- /dev/null +++ b/src/components/StatusTag/StatusTag.module.less @@ -0,0 +1,123 @@ +.statusTag { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 1px 8px; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + line-height: 20px; + white-space: nowrap; + border: 1px solid transparent; + letter-spacing: 0.2px; + + &.dimmed { + opacity: 0.55; + } + + .dot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; + } +} + +.primary { + color: #1677ff; + background: rgba(22, 119, 255, 0.08); + border-color: rgba(22, 119, 255, 0.18); + + .dot { + background: #1677ff; + box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.15); + } +} + +.success { + color: #389e0d; + background: rgba(82, 196, 26, 0.1); + border-color: rgba(82, 196, 26, 0.22); + + .dot { + background: #52c41a; + box-shadow: 0 0 0 2px rgba(82, 196, 26, 0.15); + } +} + +.warning { + color: #d48806; + background: rgba(250, 173, 20, 0.12); + border-color: rgba(250, 173, 20, 0.28); + + .dot { + background: #faad14; + box-shadow: 0 0 0 2px rgba(250, 173, 20, 0.18); + } +} + +.danger { + color: #cf1322; + background: rgba(255, 77, 79, 0.08); + border-color: rgba(255, 77, 79, 0.2); + + .dot { + background: #ff4d4f; + box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.15); + } +} + +.orange { + color: #d46b08; + background: rgba(250, 140, 22, 0.1); + border-color: rgba(250, 140, 22, 0.22); + + .dot { + background: #fa8c16; + box-shadow: 0 0 0 2px rgba(250, 140, 22, 0.15); + } +} + +.cyan { + color: #08979c; + background: rgba(19, 194, 194, 0.1); + border-color: rgba(19, 194, 194, 0.22); + + .dot { + background: #13c2c2; + box-shadow: 0 0 0 2px rgba(19, 194, 194, 0.15); + } +} + +.purple { + color: #531dab; + background: rgba(114, 46, 209, 0.08); + border-color: rgba(114, 46, 209, 0.2); + + .dot { + background: #722ed1; + box-shadow: 0 0 0 2px rgba(114, 46, 209, 0.15); + } +} + +.neutral { + color: rgba(0, 0, 0, 0.55); + background: rgba(0, 0, 0, 0.04); + border-color: rgba(0, 0, 0, 0.08); + + .dot { + background: rgba(0, 0, 0, 0.25); + box-shadow: none; + } +} + +.default { + color: rgba(0, 0, 0, 0.65); + background: #fafafa; + border-color: #f0f0f0; + + .dot { + background: rgba(0, 0, 0, 0.2); + box-shadow: none; + } +} diff --git a/src/components/StatusTag/StatusTag.tsx b/src/components/StatusTag/StatusTag.tsx new file mode 100644 index 0000000..59d47bd --- /dev/null +++ b/src/components/StatusTag/StatusTag.tsx @@ -0,0 +1,37 @@ +import { defineComponent, type PropType } from 'vue'; +import styles from './StatusTag.module.less'; + +export type StatusTagTone = + | 'primary' + | 'success' + | 'warning' + | 'danger' + | 'orange' + | 'cyan' + | 'purple' + | 'neutral' + | 'default'; + +const StatusTag = defineComponent({ + name: 'StatusTag', + props: { + label: { type: String, required: true }, + tone: { type: String as PropType, default: 'default' }, + showDot: { type: Boolean, default: true }, + dimmed: { type: Boolean, default: false }, + }, + setup(props) { + return () => ( + + {props.showDot ? : null} + {props.label} + + ); + }, +}); + +export default StatusTag; diff --git a/src/components/StatusTag/index.ts b/src/components/StatusTag/index.ts new file mode 100644 index 0000000..50b9a0f --- /dev/null +++ b/src/components/StatusTag/index.ts @@ -0,0 +1,2 @@ +export { default as StatusTag } from './StatusTag'; +export type { StatusTagTone } from './StatusTag'; diff --git a/src/components/index.ts b/src/components/index.ts index 17c5079..2afbde3 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -3,3 +3,7 @@ * 在业务中可通过 `import { HelloWorld } from '@/components'` 使用 */ export { default as HelloWorld } from './HelloWorld'; +export { default as ChangePasswordModal } from './ChangePasswordModal'; +export { default as PageTabs } from './pageTabs'; +export { StatusTag } from './StatusTag'; +export type { StatusTagTone } from './StatusTag'; diff --git a/src/components/pageTabs/index.tsx b/src/components/pageTabs/index.tsx new file mode 100644 index 0000000..14abe16 --- /dev/null +++ b/src/components/pageTabs/index.tsx @@ -0,0 +1,291 @@ +import { + defineComponent, + h, + nextTick, + onBeforeUnmount, + onMounted, + ref, + type PropType, + type VNode, + watch, +} from 'vue'; +import { Dropdown, Menu, message } from 'ant-design-vue'; +import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; +import type { IconType } from 'vue-icons-plus/lib'; +import { + LuCopyX, + LuListX, + LuMoreHorizontal, + LuPanelLeftClose, + LuPanelRightClose, +} from 'vue-icons-plus/lu'; +import type { TabView } from '@/types'; +import { useState } from '@/hooks'; +import { renderRouteIcon } from '@/utils/routeIcon'; +import styles from './style.module.less'; + +const SCROLL_STEP = 240; + +type TabKeyHandler = (key: string) => void; +type TabBatchHandler = (key: string) => void; +type TabAllHandler = () => void; + +const renderAiIcon = (Icon: IconType, size = 16) => h(Icon, { size }); + +/** + * Chrome 风格的页面标签栏组件 + * - 无滚动条,滚轮横向滚动 + * - 溢出时显示左右导航按钮 + */ +const PageTabs = defineComponent({ + name: 'PageTabs', + props: { + /** 标签列表 */ + tabs: { type: Array as PropType, required: true }, + /** 当前激活 key */ + activeKey: { type: String, required: true }, + /** 关闭标签 */ + onClose: { type: Function as PropType, default: undefined }, + /** 关闭当前标签左侧标签 */ + onCloseLeft: { type: Function as PropType, default: undefined }, + /** 关闭当前标签右侧标签 */ + onCloseRight: { type: Function as PropType, default: undefined }, + /** 关闭当前标签以外的标签 */ + onCloseOther: { type: Function as PropType, default: undefined }, + /** 关闭全部标签 */ + onCloseAll: { type: Function as PropType, default: undefined }, + /** 切换标签 */ + onChange: { type: Function as PropType, default: undefined }, + }, + setup(props) { + const scrollRef = ref(); + const tabItemRefs = ref>({}); + const [showNavLeft, setShowNavLeft] = useState(false); + const [showNavRight, setShowNavRight] = useState(false); + + let resizeObserver: ResizeObserver | null = null; + + const updateScrollState = () => { + const el = scrollRef.value; + if (!el) { + setShowNavLeft(false); + setShowNavRight(false); + return; + } + + const { scrollLeft, scrollWidth, clientWidth } = el; + const canScroll = scrollWidth - clientWidth > 1; + + setShowNavLeft(canScroll && scrollLeft > 1); + setShowNavRight(canScroll && scrollLeft + clientWidth < scrollWidth - 1); + }; + + const scrollByStep = (direction: -1 | 1) => { + const el = scrollRef.value; + if (!el) return; + el.scrollBy({ left: direction * SCROLL_STEP, behavior: 'smooth' }); + }; + + const handleWheel = (e: WheelEvent) => { + const el = scrollRef.value; + if (!el || el.scrollWidth <= el.clientWidth) return; + + const delta = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY; + if (delta === 0) return; + + e.preventDefault(); + el.scrollLeft += delta; + updateScrollState(); + }; + + const scrollActiveTabIntoView = () => { + nextTick(() => { + tabItemRefs.value[props.activeKey]?.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + inline: 'nearest', + }); + updateScrollState(); + }); + }; + + watch(() => props.activeKey, scrollActiveTabIntoView); + watch(() => props.tabs.map((t) => t.path).join(','), scrollActiveTabIntoView); + + const setTabItemRef = (path: string, el: Element | null) => { + if (el) { + tabItemRefs.value[path] = el as HTMLElement; + return; + } + + delete tabItemRefs.value[path]; + }; + + const handleClick = (key: string) => { + if (key !== props.activeKey && props.onChange) { + props.onChange(key); + } + }; + + const handleClose = (key: string, e: Event) => { + e.stopPropagation(); + if (props.tabs.length <= 1) { + message.warning('至少保留一个标签'); + return; + } + if (props.onClose) { + props.onClose(key); + } + }; + + const handleBatchClose = (key: string) => { + if (props.tabs.length <= 1) { + message.warning('至少保留一个标签'); + return; + } + + const activeIndex = props.tabs.findIndex((tab) => tab.path === props.activeKey); + if (activeIndex === -1) return; + + switch (key) { + case 'left': + if (activeIndex === 0) { + message.warning('左侧没有可关闭的标签页'); + return; + } + props.onCloseLeft?.(props.activeKey); + break; + case 'right': + if (activeIndex === props.tabs.length - 1) { + message.warning('右侧没有可关闭的标签页'); + return; + } + props.onCloseRight?.(props.activeKey); + break; + case 'other': + props.onCloseOther?.(props.activeKey); + break; + // case 'all': + // props.onCloseAll?.(); + // break; + } + }; + + onMounted(() => { + const el = scrollRef.value; + if (!el) return; + + el.addEventListener('wheel', handleWheel, { passive: false }); + el.addEventListener('scroll', updateScrollState, { passive: true }); + + resizeObserver = new ResizeObserver(updateScrollState); + resizeObserver.observe(el); + updateScrollState(); + }); + + onBeforeUnmount(() => { + const el = scrollRef.value; + if (el) { + el.removeEventListener('wheel', handleWheel); + el.removeEventListener('scroll', updateScrollState); + } + resizeObserver?.disconnect(); + resizeObserver = null; + }); + + const renderTab = (tab: TabView, i: number) => { + const isActive = tab.path === props.activeKey; + const showDivider = i !== 0 && !isActive && props.tabs[i - 1]?.path !== props.activeKey; + + return ( +

setTabItemRef(tab.path, el as Element | null)} + class={[styles.tabItem, isActive && styles.tabItemActive].filter(Boolean).join(' ')} + onClick={() => handleClick(tab.path)} + > +
+ +
+
+ + + + + + +
+ +
+ {tab.icon ? ( + {renderRouteIcon(tab.icon, { size: 16 })} + ) : null} +
{tab.title}
+
handleClose(tab.path, e)}> + ✕ +
+
+
+ ); + }; + + const renderBatchMenuItem = (key: string, icon: VNode, label: string) => ( + +
+ {icon} + {label} +
+
+ ); + + return () => ( +
+ {showNavLeft.value && ( + + )} + +
+ {props.tabs.map((tab, i) => renderTab(tab, i))} +
+ + {showNavRight.value && ( + + )} + + + {{ + default: () => ( + + ), + overlay: () => ( + handleBatchClose(String(key))}> + {renderBatchMenuItem('left', renderAiIcon(LuPanelLeftClose), '关闭左侧标签页')} + {renderBatchMenuItem('right', renderAiIcon(LuPanelRightClose), '关闭右侧标签页')} + {renderBatchMenuItem('other', renderAiIcon(LuCopyX), '关闭其它标签页')} + {/* {renderBatchMenuItem('all', renderAiIcon(LuListX), '关闭全部标签页')} */} + + ), + }} + +
+ ); + }, +}); + +export default PageTabs; diff --git a/src/components/pageTabs/style.module.less b/src/components/pageTabs/style.module.less new file mode 100644 index 0000000..4c9957f --- /dev/null +++ b/src/components/pageTabs/style.module.less @@ -0,0 +1,290 @@ +@primary: #1677ff; + +.pageTabsWrap { + position: relative; + display: flex; + align-items: center; + width: 100%; + height: 100%; + + .navBtn { + position: absolute; + top: 50%; + z-index: 5; + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: none; + border-radius: 6px; + background: #fff; + color: rgba(0, 0, 0, 0.45); + box-shadow: 0 0 4px rgba(214, 214, 214, 0.9); + cursor: pointer; + transform: translateY(-50%); + transition: + color 0.15s, + background-color 0.15s; + + &:hover { + color: rgba(0, 0, 0, 0.88); + background: #f5f5f5; + } + + &:active { + color: rgba(0, 0, 0, 0.88); + background: #ebebeb; + } + } + + .navBtnLeft { + left: 0; + } + + .navBtnRight { + right: 34px; + } + + .batchBtn { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + margin-left: 6px; + padding: 0; + border: none; + border-radius: 6px; + background: #fff; + color: rgba(0, 0, 0, 0.45); + box-shadow: 0 0 4px rgba(214, 214, 214, 0.9); + cursor: pointer; + transition: + color 0.15s, + background-color 0.15s; + + &:hover { + color: rgba(0, 0, 0, 0.88); + background: #f5f5f5; + } + + &:active { + color: rgba(0, 0, 0, 0.88); + background: #ebebeb; + } + } + + .pageTabs { + display: flex; + flex: 1; + align-items: flex-start; + box-sizing: border-box; + min-width: 0; + width: auto; + height: 100%; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } + + .tabItem { + position: relative; + display: flex; + flex-shrink: 0; + align-items: center; + height: 100%; + user-select: none; + transition: all 0.15s ease; + + &:not(:first-child) { + margin-left: -10px; + } + + &:global(:not(.dragging)) { + cursor: pointer; + } + + &:not(.tabItemActive) { + &:hover { + > .divider { + opacity: 0; + } + + & + .tabItem { + > .divider { + opacity: 0; + } + } + + .tabContent { + background-color: rgba(0, 0, 0, 0.06); + } + } + + .tabClose:hover { + color: rgba(0, 0, 0, 0.85); + } + } + + &.tabItemActive { + z-index: 2; + + & + .tabItem { + > .divider { + opacity: 0 !important; + } + } + + .tabTitle { + color: @primary; + font-weight: 500; + } + + .tabIcon { + color: @primary; + } + + .tabBg { + &.tabBgActive { + .tabBgInner { + background-color: fade(@primary, 8%); + } + + .tabBgCurveBefore, + .tabBgCurveAfter { + fill: fade(@primary, 8%); + } + } + } + } + } + + .divider { + position: absolute; + left: 8px; + top: 50%; + z-index: 0; + width: 1px; + height: 18px; + background-color: #e8e8e8; + transform: translateY(-50%); + transition: opacity 0.15s; + } + + .tabBg { + position: absolute; + top: 0; + left: 0; + z-index: -1; + width: 100%; + height: 100%; + padding: 0 calc(8px - 1px); + transition: opacity 0.15s; + } + + .tabBgInner { + height: 100%; + border-radius: 8px 8px 0 0; + transition: + background-color 0.15s, + margin 0.15s, + border-radius 0.15s; + } + + .tabBgCurveBefore { + position: absolute; + left: -1px; + bottom: 0; + fill: transparent; + transition: fill 0.15s; + } + + .tabBgCurveAfter { + position: absolute; + right: -1px; + bottom: 0; + fill: transparent; + transition: fill 0.15s; + } + + .tabContent { + position: relative; + height: 34px; + z-index: 2; + display: inline-flex; + align-items: center; + gap: 6px; + width: fit-content; + flex-shrink: 0; + margin: 0 12px; + padding: 4px 4px 4px 10px; + border-radius: 9px; + transition: background-color 0.35s; + } + + .tabIcon { + display: inline-flex; + align-items: center; + flex-shrink: 0; + color: rgba(0, 0, 0, 0.45); + line-height: 1; + font-size: 16px; + } + + .tabTitle { + min-width: 0; + max-width: 160px; + font-size: 14px; + line-height: 1.4; + color: rgba(0, 0, 0, 0.65); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .tabClose { + flex-shrink: 0; + width: 18px; + height: 18px; + line-height: 18px; + font-size: 11px; + text-align: center; + color: rgba(0, 0, 0, 0.45); + border-radius: 50%; + cursor: pointer; + transition: all 0.15s; + } + } +} + +.batchMenuItem { + display: flex; + align-items: center; + padding: 4px 0; + gap: 8px; + line-height: 1; +} + +.batchMenuItem + .batchMenuItem { + margin-top: 4px; +} + +.batchMenuIcon { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + line-height: 1; + + > svg { + display: block; + } +} diff --git a/src/config/fallbackRoutes.ts b/src/config/fallbackRoutes.ts new file mode 100644 index 0000000..67eb757 --- /dev/null +++ b/src/config/fallbackRoutes.ts @@ -0,0 +1,187 @@ +import type { MenuNode } from '@/types'; + +/** + * ====== 兜底路由配置 ====== + * + * 当后端菜单接口不可用时,使用此配置作为降级方案。 + * 数据格式与后端 `/menu/tree` 接口返回完全一致, + * 因此可以直接通过 transformMenuToRoutes / transformMenuNode 复用同一套处理逻辑。 + * + * ## 后端应返回的数组格式示例 + * + * ```json + * [ + * { "id": "dashboard", "name": "工作台", "path": "dashboard", "component": "dashboard", "icon": "AiOutlineDashboard" }, + * { + * "id": "events", "name": "赛事管理", "path": "events", "icon": "AiOutlineTrophy", + * "children": [ + * { "id": "events_list", "name": "赛事列表", "path": "events/list", "component": "events/list", "icon": "AiOutlineUnorderedList" } + * ] + * } + * ] + * ``` + * + * ### 字段说明 + * | 字段 | 类型 | 必填 | 说明 | + * |------|------|------|------| + * | id | number\|string | 是 | 唯一标识 | + * | name | string | 是 | 菜单名称/路由 name | + * | path | string | 是 | 路由相对路径(不带 / 前缀) | + * | component | string | 否 | 组件路径(映射 src/pages/ 下目录,如 "events/list") | + * | icon | string | 否 | vue-icons-plus 图标名(如 AiOutlineTrophy,需在 routeIcon.tsx 注册) | + * | sort | number | 否 | 排序权重(越小越前) | + * | hideInMenu | boolean | 否 | 是否在菜单中隐藏 | + * | externalLink | string | 否 | 外链地址 | + * | activeMenu | string | 否 | 高亮菜单路径 | + * | disabled | boolean | 否 | 禁用标记(禁用后路由不注册、菜单不显示) | + * | permission | string | 否 | 页面查看权限码(如 'events.list.view'),用于动态权限过滤菜单 | + * | children | MenuNode[] | 否 | 子路由 | + * + * ### 关于 routes → menuItems 的转换 + * `menuStore.ts` 中的 `transformMenuNode()` 已完成此功能, + * 它将 MenuNode[] 递归转换为 antd Menu 组件所需的 MenuItemRaw[] 格式。 + */ + +export const FALLBACK_MENU_NODES: MenuNode[] = [ + { + id: 'events', + name: '赛事管理', + path: 'events', + icon: 'AiOutlineTrophy', + children: [ + { + id: 'events_list', + name: '赛事列表', + path: 'events/list', + component: 'events/list', + componentName: 'EventList', + icon: 'AiOutlineUnorderedList', + permission: 'events.list.view', + }, + { + id: 'events_orders', + name: '订单管理', + path: 'events/orders', + component: 'events/orders', + componentName: 'EventOrders', + icon: 'AiOutlineShoppingCart', + permission: 'events.orders.view', + }, + { + id: 'events_users', + name: '用户列表', + path: 'events/users', + component: 'events/users', + componentName: 'EventUsers', + icon: 'AiOutlineTeam', + permission: 'events.users.view', + }, + { + id: 'events_banner', + name: 'Banner配置', + path: 'events/banner', + component: 'events/banner', + componentName: 'EventBanner', + icon: 'AiOutlinePicture', + permission: 'events.banner.view', + }, + { + id: 'events_logs', + name: '赛事操作日志', + path: 'events/logs', + component: 'events/logs', + componentName: 'EventLogs', + icon: 'AiOutlineHistory', + permission: 'events.logs.view', + }, + ], + }, + { + id: 'finance', + name: '账务管理', + path: 'finance', + icon: 'AiOutlineTransaction', + children: [ + { + id: 'finance_withdraw', + name: '提现申请', + path: 'finance/withdraw', + component: 'finance/withdraw', + componentName: 'FinanceWithdraw', + icon: 'AiOutlineMoneyCollect', + permission: 'finance.withdraw.view', + }, + { + id: 'finance_wallet', + name: '用户钱包', + path: 'finance/wallet', + component: 'finance/wallet', + componentName: 'FinanceWallet', + icon: 'AiOutlineWallet', + permission: 'finance.wallet.view', + }, + { + id: 'finance_payments', + name: '支付流水', + path: 'finance/payments', + component: 'finance/payments', + componentName: 'FinancePayments', + icon: 'AiOutlinePayCircle', + permission: 'finance.payments.view', + }, + { + id: 'finance_reports', + name: '账务报表', + path: 'finance/reports', + component: 'finance/reports', + componentName: 'FinanceReports', + icon: 'AiOutlineBarChart', + permission: 'finance.reports.view', + }, + ], + }, + { + id: 'system', + name: '系统管理', + path: 'system', + icon: 'AiOutlineSetting', + children: [ + { + id: 'system_users', + name: '用户管理', + path: 'system/users', + component: 'system/users', + componentName: 'SystemUsers', + icon: 'AiOutlineUser', + permission: 'system.users.view', + }, + { + id: 'system_roles', + name: '角色权限', + path: 'system/roles', + component: 'system/roles', + componentName: 'SystemRoles', + icon: 'AiOutlineSafetyCertificate', + permission: 'system.roles.view', + }, + { + id: 'system_params', + name: '参数配置', + path: 'system/params', + component: 'system/params', + componentName: 'SystemParams', + icon: 'AiOutlineSetting', + permission: 'system.params.view', + }, + { + id: 'system_logs', + name: '操作日志', + path: 'system/logs', + component: 'system/logs', + componentName: 'SystemLogs', + icon: 'AiOutlineAudit', + permission: 'system.logs.view', + }, + ], + }, +]; diff --git a/src/config/mock.ts b/src/config/mock.ts new file mode 100644 index 0000000..200af80 --- /dev/null +++ b/src/config/mock.ts @@ -0,0 +1,12 @@ +/** + * 全局假数据开关 + * + * true = 所有 API 使用假数据(模拟真实网络延时 2s 后返回假数据) + * false = 走真实后端接口 + * + * 对接后端接口后将此值改为 false 即可切换。 + */ +export const USE_MOCK = false; + +/** 模拟网络延时(毫秒) */ +export const MOCK_DELAY = 850; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 78a5410..11f8541 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -7,4 +7,6 @@ export * from './useAuth'; export * from './useWebSocket'; export * from './useBasicLayout'; export * from './useBreadcrumb'; +export * from './useThrottleFn'; +export * from './useContainerSize'; export * from './useContext'; diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 5eca581..51efda7 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -1,53 +1,81 @@ // src/hooks/useAuth.ts +import { notification } from 'ant-design-vue'; import { useState } from './useState'; -import { watch } from 'vue'; +import { useEffect } from './useEffect'; import { useMenuStore } from '@/stores/menuStore'; import { usePermissionStore } from '@/stores/permissionStore'; +import { useTabsStore } from '@/stores/tabsStore'; +import { useUserStore } from '@/stores/userStore'; +import { clearAuthDataCache } from '@/api/authCache'; +import { logout as logoutApi } from '@/api/login'; import router from '@/router'; const TOKEN_KEY = 'MY_APP_AUTH_TOKEN'; +let isForceLoggingOut = false; + function createAuth() { const [token, setToken] = useState(window.localStorage.getItem(TOKEN_KEY)); - watch(token, (newToken) => { + useEffect(() => { + const newToken = token.value; if (newToken) { window.localStorage.setItem(TOKEN_KEY, newToken); } else { window.localStorage.removeItem(TOKEN_KEY); } - }); + }, [token]); const isLoggedIn = () => !!token.value; + const clearSession = () => { + setToken(null); + + const { clearUser } = useUserStore(); + const { clearMenu } = useMenuStore(); + const { clearPermissions } = usePermissionStore(); + const { clearTabs } = useTabsStore(); + + clearUser(); + clearTabs(); + clearMenu(); + clearPermissions(); + clearAuthDataCache(); + }; + /** * 登录:保存 token → 拉取菜单+权限 → 动态注册路由 → 跳转首页 */ - const login = async (newToken: string) => { + const login = async (newToken: string, targetPath?: string) => { + isForceLoggingOut = false; setToken(newToken); - const { loadMenu, homePath } = useMenuStore(); + const { loadMenu, homePath, isRoutePathAvailable } = useMenuStore(); const { loadPermissions } = usePermissionStore(); // 并行拉取菜单和权限 await Promise.all([loadMenu(), loadPermissions()]); - // 跳转到首页(动态路由已注册完成) - router.push(homePath.value); + // 权限获取失败时会清空会话,此时不再跳转首页 + if (!isLoggedIn()) return; + + const nextPath = + targetPath && isRoutePathAvailable(targetPath) ? targetPath : homePath.value || '/404'; + + // 动态路由注册完成后,再跳转到有效目标页 + router.push(nextPath); }; /** - * 退出:清除 token → 清除菜单+权限 → 清除动态路由 → 跳转登录页 + * 退出:调用登出接口 → 清除 token → 清除菜单+权限 → 清除动态路由 → 跳转登录页 */ - const logout = () => { - setToken(null); - - const { clearMenu } = useMenuStore(); - const { clearPermissions } = usePermissionStore(); - - clearMenu(); - clearPermissions(); - + const logout = async () => { + try { + await logoutApi(); + } catch { + // 即使接口失败也要继续清除本地会话 + } + clearSession(); router.push('/login'); }; @@ -56,7 +84,25 @@ function createAuth() { isLoggedIn, login, logout, + clearSession, }; } export const auth = createAuth(); + +/** + * 强制重新登录:提示错误 → 清空会话 → 跳转登录页 + * 用于权限获取失败、登录态失效等需要彻底重置会话的场景 + */ +export function forceReLogin(msg = '账户权限获取失败,请重新登录') { + if (isForceLoggingOut) return; + isForceLoggingOut = true; + + notification.error({ + message: '提示', + description: msg, + duration: 3, + }); + auth.clearSession(); + router.push('/login'); +} diff --git a/src/hooks/useContainerSize.ts b/src/hooks/useContainerSize.ts new file mode 100644 index 0000000..8bf68e4 --- /dev/null +++ b/src/hooks/useContainerSize.ts @@ -0,0 +1,54 @@ +import { ref, onMounted, onUnmounted, type Ref } from 'vue'; + +/** + * 监听容器尺寸变化,返回动态 width/height 给 Table 的 scroll 属性使用。 + * + * 基于 ResizeObserver,窗口缩放 / 侧边栏收起展开 / 筛选区折叠 等任何导致 + * 容器尺寸变化的行为都会自动触发重新计算,无需额外处理。 + * + * 用法: + * ```ts + * const { containerRef, width, height } = useContainerSize(); + * //
+ * // + * // + * ``` + * + * @param headerOffset 从容器高度中扣除的表头偏移量,默认 55 + */ +export function useContainerSize(options?: { headerOffset?: number }) { + const { headerOffset = 55 } = options ?? {}; + + const containerRef = ref(null) as Ref; + const width = ref(0); + const height = ref(0); + + let observer: ResizeObserver | null = null; + + const updateSize = () => { + const el = containerRef.value; + if (!el) return; + width.value = el.clientWidth; + height.value = Math.max(el.clientHeight - headerOffset, 200); + }; + + onMounted(() => { + updateSize(); + const el = containerRef.value; + if (!el) return; + + observer = new ResizeObserver((entries) => { + for (const entry of entries) { + const w = entry.contentRect.width; + const h = entry.contentRect.height; + if (w !== width.value) width.value = w; + if (h !== height.value) height.value = Math.max(h - headerOffset, 200); + } + }); + observer.observe(el); + }); + + onUnmounted(() => observer?.disconnect()); + + return { containerRef, width, height }; +} diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts index aebfad1..30393d1 100644 --- a/src/hooks/useDebounce.ts +++ b/src/hooks/useDebounce.ts @@ -1,6 +1,7 @@ // src/hooks/useDebounce.ts -import { watch, onUnmounted } from 'vue'; +import { onUnmounted, Ref } from 'vue'; import { useState } from './useState'; +import { useEffect } from './useEffect'; export interface UseDebounceOptions { delay?: number; @@ -8,14 +9,11 @@ export interface UseDebounceOptions { maxWait?: number; } -export function useDebounce( - source: any, // Ref - options: number | UseDebounceOptions = {}, -) { +export function useDebounce(source: Ref, options: number | UseDebounceOptions = {}) { const config = typeof options === 'number' ? { delay: options } : options; const { delay = 300, immediate = false, maxWait } = config; - const [debouncedValue, setDebouncedValue] = useState(source.value); + const [debouncedValue, setDebouncedValue] = useState(source.value as T); const [isPending, setIsPending] = useState(false); let timer: any = null; @@ -43,7 +41,7 @@ export function useDebounce( isFirstCall = true; }; - watch(source, () => { + useEffect(() => { setIsPending(true); // 处理 maxWait @@ -64,7 +62,9 @@ export function useDebounce( updateValue(); }, delay); } - }); + + return clearTimers; + }, [source]); // 卸载 onUnmounted(() => { diff --git a/src/hooks/usePagination.ts b/src/hooks/usePagination.ts index eeafc3c..c44340a 100644 --- a/src/hooks/usePagination.ts +++ b/src/hooks/usePagination.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ // src/hooks/usePagination.ts -import { computed, ref, watch, Ref, triggerRef } from 'vue'; +import { computed, ref, Ref, triggerRef } from 'vue'; +import { useEffect } from './useEffect'; /** * 分页配置项 @@ -77,24 +78,20 @@ export function usePagination(options: UsePaginationOptions = {}): UsePagination const pageSizeRef = ref(options.pageSize ?? defaultPageSize); const totalRef = ref(options.total ?? 0); - watch( - () => options.current, - (val) => { - if (val !== undefined) currentRef.value = val; - }, - ); - watch( - () => options.pageSize, - (val) => { - if (val !== undefined) pageSizeRef.value = val; - }, - ); - watch( - () => options.total, - (val) => { - if (val !== undefined) totalRef.value = val; - }, - ); + useEffect(() => { + const val = options.current; + if (val !== undefined) currentRef.value = val; + }, [() => options.current]); + + useEffect(() => { + const val = options.pageSize; + if (val !== undefined) pageSizeRef.value = val; + }, [() => options.pageSize]); + + useEffect(() => { + const val = options.total; + if (val !== undefined) totalRef.value = val; + }, [() => options.total]); const totalPages = computed(() => { const total = totalRef.value; @@ -183,3 +180,13 @@ export function usePagination(options: UsePaginationOptions = {}): UsePagination refresh, }; } + +/** 将接口返回的分页 total 同步到 pagination(支持 number / 数字字符串,含 0) */ +export function syncPaginationTotal( + pagination: Pick, + total: unknown, +): void { + if (total == null || total === '') return; + const n = Number(total); + if (Number.isFinite(n)) pagination.setTotal(n); +} diff --git a/src/hooks/useRequest.ts b/src/hooks/useRequest.ts index bd1076c..ce984e7 100644 --- a/src/hooks/useRequest.ts +++ b/src/hooks/useRequest.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ref, Ref, watch, WatchSource, onUnmounted, computed } from 'vue'; +import { ref, Ref, WatchSource, onUnmounted, computed } from 'vue'; +import { useEffect } from './useEffect'; export interface UseRequestReturn { /** 纯净的数据,不包含后端外层壳 */ @@ -128,31 +129,29 @@ export function useRequest( const shouldAutoRun = computed(() => !manual && ready.value); let hasAutoRun = false; // 标记是否已自动执行过,防止重复触发 - watch( - shouldAutoRun, - (val) => { - if (val && !hasAutoRun) { - hasAutoRun = true; - run(); - } else if (!val) { - // 当条件不满足时重置 hasAutoRun,使下次满足条件时能再次自动执行 - hasAutoRun = false; - } - }, - { immediate: true }, - ); + useEffect(() => { + const val = shouldAutoRun.value; + if (val && !hasAutoRun) { + hasAutoRun = true; + run(); + } else if (!val) { + // 当条件不满足时重置 hasAutoRun,使下次满足条件时能再次自动执行 + hasAutoRun = false; + } + }, [shouldAutoRun]); // refreshDeps 变化时重新请求(初始化时不再重复触发) if (refreshDeps && refreshDeps.length > 0) { - watch( - refreshDeps, - () => { - if (shouldAutoRun.value) { - run(); - } - }, - { deep: true }, - ); + let skipInitialRefresh = true; + useEffect(() => { + if (skipInitialRefresh) { + skipInitialRefresh = false; + return; + } + if (shouldAutoRun.value) { + run(); + } + }, refreshDeps); } const getSignal = () => abortController?.signal; diff --git a/src/hooks/useThrottleFn.ts b/src/hooks/useThrottleFn.ts new file mode 100644 index 0000000..9b1ce8e --- /dev/null +++ b/src/hooks/useThrottleFn.ts @@ -0,0 +1,46 @@ +import { ref } from 'vue'; + +/** + * 节流函数封装 + * @param fn 需要节流的函数 + * @param delay 节流间隔时间(ms),默认 500 + * @returns 返回节流后的函数,连续调用时只会在每个 delay 周期内执行一次 + */ +export function useThrottleFn any>( + fn: T, + delay: number = 500, +): T & { cancel: () => void } { + const lastTime = ref(0); + let timer: any = null; + + const throttled = function (this: any, ...args: any[]) { + const now = Date.now(); + const remaining = delay - (now - lastTime.value); + + if (remaining <= 0) { + // 冷却时间已过,立即执行 + if (timer) { + clearTimeout(timer); + timer = null; + } + lastTime.value = now; + fn.apply(this, args); + } else if (!timer) { + // 冷却时间未过,延后执行 + timer = setTimeout(() => { + lastTime.value = Date.now(); + timer = null; + fn.apply(this, args); + }, remaining); + } + } as T & { cancel: () => void }; + + throttled.cancel = () => { + if (timer) { + clearTimeout(timer); + timer = null; + } + }; + + return throttled; +} diff --git a/src/hooks/useWebSocket.ts b/src/hooks/useWebSocket.ts index 2952105..4cde23b 100644 --- a/src/hooks/useWebSocket.ts +++ b/src/hooks/useWebSocket.ts @@ -1,5 +1,6 @@ -import { ref, watch, onBeforeUnmount, toValue } from 'vue'; +import { ref, onBeforeUnmount, toValue } from 'vue'; import type { Ref, MaybeRefOrGetter } from 'vue'; +import { useEffect } from './useEffect'; // --- 类型定义 --- export type WebSocketStatus = 'connecting' | 'open' | 'closed' | 'error'; @@ -216,14 +217,16 @@ export function useWebSocket( // --- 监听与副作用 --- // 监听 URL 变化,如果变了则重新连接 - watch( - () => toValue(url), - () => { - if (status.value !== 'closed') { - connect(); - } - }, - ); + let skipInitialUrlWatch = true; + useEffect(() => { + if (skipInitialUrlWatch) { + skipInitialUrlWatch = false; + return; + } + if (status.value !== 'closed') { + connect(); + } + }, [() => toValue(url)]); // 组件卸载时清理资源 onBeforeUnmount(() => { diff --git a/src/layouts/BasicLayout.module.less b/src/layouts/BasicLayout.module.less index a3fc109..bf59f0e 100644 --- a/src/layouts/BasicLayout.module.less +++ b/src/layouts/BasicLayout.module.less @@ -1,57 +1,141 @@ -.container { +.basicLayoutMain { min-height: 100vh; -} + height: 100vh; -.sider { - box-shadow: 2px 0 8px rgba(0, 0, 0, 0.08); -} + .logo { + height: 56px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + color: #1a1a1a; + background: #fff; + } -.logo { - height: 56px; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; - color: #fff; - background: rgba(255, 255, 255, 0.08); -} + .logoText { + font-size: 16px; + font-weight: 600; + white-space: nowrap; + } -.logoText { - font-size: 16px; - font-weight: 600; - white-space: nowrap; -} + .trigger { + font-size: 18px; + cursor: pointer; + transition: color 0.3s; + padding: 0 12px; + display: inline-flex; + align-items: center; -.header { - background: #fff; - padding: 0 16px; - display: flex; - align-items: center; - box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); -} + &:hover { + color: #1677ff; + } + } -.trigger { - font-size: 18px; - cursor: pointer; - transition: color 0.3s; - padding: 0 12px; - display: inline-flex; - align-items: center; + .breadcrumb { + flex: 1; + margin-left: 8px; + font-size: 14px; - &:hover { - color: #1677ff; + :global { + .ant-breadcrumb-link, + .ant-breadcrumb-separator, + .ant-breadcrumb-item a { + color: rgba(0, 0, 0, 0.65); + } + + .ant-breadcrumb-item:last-child { + .ant-breadcrumb-link, + .ant-breadcrumb-separator, + a { + color: rgba(0, 0, 0, 0.88); + font-weight: 500; + cursor: default; + } + } + } + } + + .headerRight { + margin-left: auto; + display: flex; + gap: 10px; + align-items: center; + } + + .displayName { + font-size: 14px; + color: rgba(0, 0, 0, 0.65); + } + + .logoutIcon { + width: 18px; + height: 18px; + opacity: 0.45; + cursor: pointer; + transition: opacity 0.3s; + + &:hover { + opacity: 1; + } + } + + .content { + margin: 16px; + border-radius: 8px; + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + overflow: hidden; + } + + .pageView { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden; + display: flex; + flex-direction: column; + } + + .main { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + } + + .headerRow { + padding: 0 16px; + width: 100%; + height: 48px; + display: flex; + align-items: center; + } + + .pageTabs { + width: 100%; + height: 48px; + padding: 4px 8px 0; + background: #fff; + border-top: 1px solid #f0f0f0; } } -.content { - margin: 16px; - padding: 24px; - background: #fff; - border-radius: 8px; - min-height: 280px; +/** + * Dropdown 菜单通过 portal 挂到 body,不在 .basicLayoutMain 内。 + * 若嵌套在 .basicLayoutMain 下,CSS Modules 会生成「父级 + 子级」选择器,导致匹配失败。 + */ +.menuItem { + display: inline-flex; + align-items: center; + gap: 8px; + + :global(.anticon) { + font-size: 14px; + } } -.footer { - text-align: center; - color: rgba(0, 0, 0, 0.45); +.menuItemDanger { + color: #ff4d4f; } diff --git a/src/layouts/BasicLayout.tsx b/src/layouts/BasicLayout.tsx index 00e4baa..d502668 100644 --- a/src/layouts/BasicLayout.tsx +++ b/src/layouts/BasicLayout.tsx @@ -1,33 +1,52 @@ -import { computed, defineComponent, h, ref } from 'vue'; +import { computed, defineComponent, h, Transition, KeepAlive } from 'vue'; +import type { CSSProperties } from 'vue'; import { useRouter, useRoute, RouterView } from 'vue-router'; -import { Layout, Menu } from 'ant-design-vue'; +import { Layout, Menu, Modal, Breadcrumb, Dropdown, message } from 'ant-design-vue'; import type { MenuProps } from 'ant-design-vue'; import { - DashboardOutlined, - InfoCircleOutlined, + EditOutlined, + LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, - SettingOutlined, } from '@ant-design/icons-vue'; import { useMenuStore } from '@/stores/menuStore'; +import { useUserStore } from '@/stores/userStore'; +import { useTabsStore } from '@/stores/tabsStore'; +import { useState, useBreadcrumb, auth } from '@/hooks'; +import { ChangePasswordModal, PageTabs } from '@/components'; +import { updateUserPwd } from '@/api/users'; +import { renderRouteIcon } from '@/utils/routeIcon'; +import logoutSvg from '@/assets/img/icon/logout.svg'; import styles from './BasicLayout.module.less'; const { Header, Sider, Content, Footer } = Layout; -/** - * 图标名 → 组件映射 - * 后端下发的 icon 字符串在此映射为 antd icon 组件 - * 扩展新图标只需在此添加一行 - */ -const ICON_MAP: Record = { - DashboardOutlined, - InfoCircleOutlined, - SettingOutlined, +const headerStyle: CSSProperties = { + background: '#fff', + padding: '0', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + color: '#1a1a1a', + boxShadow: '0 1px 4px rgba(0, 21, 41, 0.08)', + height: 'auto', +}; + +const siderStyle: CSSProperties = { + background: '#fff', + boxShadow: '2px 0 8px rgba(0, 21, 41, 0.08)', +}; + +const footerStyle: CSSProperties = { + padding: '0 50px 16px 50px', + textAlign: 'center', + color: '#3a3a3a', + userSelect: 'none', }; /** * 将 menuStore 中的 menuItems 转为 antd Menu 的 items 格式 - * 递归处理 iconName → icon VNode + * 递归处理 iconName → vue-icons-plus 图标 VNode */ function resolveMenuItems(items: any[]): MenuProps['items'] { return items.map((item) => { @@ -36,9 +55,8 @@ function resolveMenuItems(items: any[]): MenuProps['items'] { label: item.label, }; - // iconName 字符串转为 VNode - if (item.iconName && ICON_MAP[item.iconName]) { - resolved.icon = () => h(ICON_MAP[item.iconName]); + if (item.iconName) { + resolved.icon = () => renderRouteIcon(item.iconName, { size: 16 }); } if (item.children?.length) { @@ -57,9 +75,21 @@ export default defineComponent({ setup() { const route = useRoute(); const router = useRouter(); - const collapsed = ref(false); + const [collapsed, setCollapsed] = useState(false); const { menuItems } = useMenuStore(); + const { phone, nickname } = useUserStore(); + const displayName = computed(() => phone.value || nickname.value); + const { + tabs, + cachedViews, + removeTab, + removeLeftTabs, + removeRightTabs, + removeOtherTabs, + removeAllTabs, + } = useTabsStore(); + const { breadcrumbs } = useBreadcrumb(); const selectedKeys = computed(() => [route.path]); @@ -70,17 +100,100 @@ export default defineComponent({ }; const toggleCollapsed = () => { - collapsed.value = !collapsed.value; + setCollapsed(!collapsed.value); + }; + + const activeTabKey = computed(() => route.path); + + /** ===== 退出登录 ===== */ + const handleLogout = () => { + Modal.confirm({ + title: '确认退出', + content: '确认要退出当前账号吗?', + okText: '确认', + cancelText: '取消', + onOk: () => { + auth.logout(); + }, + }); + }; + + /** ===== 修改密码弹窗 ===== */ + const [changePwdVisible, setChangePwdVisible] = useState(false); + const [changePwdSubmitting, setChangePwdSubmitting] = useState(false); + + const handleChangePassword = () => { + setChangePwdVisible(true); + }; + + const handleChangePwdSave = async (payload: { newPassword: string }) => { + if (changePwdSubmitting.value) return; + setChangePwdSubmitting(true); + try { + const res = await updateUserPwd({ + password: payload.newPassword, + confirmPassword: payload.newPassword, + }); + if (res.code == 200) { + message.success('密码修改成功'); + setChangePwdVisible(false); + window.setTimeout(() => { + auth.logout(); + }, 1500); + } else { + message.error(res.msg || '修改失败'); + } + } catch (err: any) { + console.error('修改密码失败:', err); + } finally { + setChangePwdSubmitting(false); + } + }; + + /** ===== 头像下拉菜单:修改密码 / 退出登录 ===== */ + const handleUserMenuClick: MenuProps['onClick'] = ({ key }) => { + if (key === 'changePassword') { + handleChangePassword(); + } else if (key === 'logout') { + handleLogout(); + } + }; + + /** ===== PageTabs 事件 ===== */ + const handleTabChange = (key: string) => { + if (key !== route.path) { + router.push(key); + } + }; + + const handleTabClose = (key: string) => { + removeTab(key); + }; + + const handleCloseLeftTabs = (key: string) => { + removeLeftTabs(key); + }; + + const handleCloseRightTabs = (key: string) => { + removeRightTabs(key); + }; + + const handleCloseOtherTabs = (key: string) => { + removeOtherTabs(key); + }; + + const handleCloseAllTabs = () => { + removeAllTabs(); }; return () => ( - - + +
- {collapsed.value ? '6羽' : '六个羽友赛事'} + {collapsed.value ? '6羽' : '六羽赛事运营平台'}
- -
- - {collapsed.value ? : } - + +
+ {/* + ── 顶部行:折叠按钮 + 面包屑 + 右侧账号信息 ── + 按需求把原来平铺在 Header 里的三块统一包到一个 row 容器内, + 形成「行容器 + tabs 容器」的两层结构 + */} +
+ + {collapsed.value ? : } + + + {/* 面包屑:紧跟在折叠按钮之后 */} + + {breadcrumbs.value.map((item, index) => { + const isLast = index === breadcrumbs.value.length - 1; + return ( + + {isLast || !item.path ? ( + {item.title} + ) : ( + router.push(item.path as string)}>{item.title} + )} + + ); + })} + + + {/* 右侧: 账号信息 + 退出 */} +
+ {displayName.value} + + {{ + default: () => , + overlay: () => ( + + + + + 修改密码 + + + + + + + 退出登录 + + + + ), + }} + +
+
+ + {/* + ── Tabs 行:Chrome 风格页面标签栏 ── + 数据来自 tabsStore,点击切换路由,关闭按钮走 store.removeTab + */} +
+ +
+ {/* + ── 路由出口 ── + 用 缓存已访问过的页面, + include 是组件 name 列表,所以页面组件需要在 defineComponent 中显式给出 name + */} - +
+ ( + + + {Component ? h(Component, { key: route.path }) : null} + + + ), + }} + /> +
-
六个羽友赛事运营平台 ©{new Date().getFullYear()}
+ {/*
六个羽友赛事运营平台 ©{new Date().getFullYear()}
*/}
+ + {/* 修改密码弹窗 */} + {changePwdVisible.value && ( + setChangePwdVisible(false)} + onSave={handleChangePwdSave} + /> + )} ); }, diff --git a/src/main.ts b/src/main.ts index d723be9..2c16dae 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,10 +6,13 @@ import App from './App'; import router from './router'; import './router/guard'; // 路由守卫:登录鉴权 + 动态菜单加载 import './assets/styles/index.less'; +import ypComponent from '@yp-component/root'; +import '@yp-component/root/style.css'; const app = createApp(App); app.use(router); app.use(Antd); +app.use(ypComponent); app.mount('#app'); diff --git a/src/pages/about/index.module.less b/src/pages/about/index.module.less index c52f57c..14af90d 100644 --- a/src/pages/about/index.module.less +++ b/src/pages/about/index.module.less @@ -1,20 +1,18 @@ -.container { - // 容器 -} +.aboutMain { + .title { + margin-top: 0; + margin-bottom: 24px; + font-size: 20px; + font-weight: 600; + } -.title { - margin-top: 0; - margin-bottom: 24px; - font-size: 20px; - font-weight: 600; -} + .subtitle { + margin: 16px 0 8px; + } -.subtitle { - margin: 16px 0 8px; -} - -.list { - margin: 0; - padding-left: 20px; - line-height: 2; + .list { + margin: 0; + padding-left: 20px; + line-height: 2; + } } diff --git a/src/pages/about/index.tsx b/src/pages/about/index.tsx index b4e7084..be3e1e1 100644 --- a/src/pages/about/index.tsx +++ b/src/pages/about/index.tsx @@ -23,7 +23,7 @@ export default defineComponent({ ]; return () => ( -
+

关于

CPMS 运营平台前端 PC 端。

diff --git a/src/pages/dashboard/index.module.less b/src/pages/dashboard/index.module.less index e816330..3ebc662 100644 --- a/src/pages/dashboard/index.module.less +++ b/src/pages/dashboard/index.module.less @@ -1,27 +1,25 @@ -.container { - // 容器 -} - -.title { - margin-top: 0; - margin-bottom: 24px; - font-size: 20px; - font-weight: 600; -} - -.panel { - margin-top: 16px; - - ul { - margin: 12px 0 0; - padding-left: 20px; - line-height: 2; +.dashboardMain { + .title { + margin-top: 0; + margin-bottom: 24px; + font-size: 20px; + font-weight: 600; } - code { - padding: 2px 8px; - background: #f5f5f5; - border-radius: 4px; - font-size: 13px; + .panel { + margin-top: 16px; + + ul { + margin: 12px 0 0; + padding-left: 20px; + line-height: 2; + } + + code { + padding: 2px 8px; + background: #f5f5f5; + border-radius: 4px; + font-size: 13px; + } } } diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 489e275..4a93e13 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -23,7 +23,7 @@ export default defineComponent({ ]; return () => ( -
+

工作台

diff --git a/src/pages/events/banner/components/BannerFormModal.module.less b/src/pages/events/banner/components/BannerFormModal.module.less new file mode 100644 index 0000000..1306990 --- /dev/null +++ b/src/pages/events/banner/components/BannerFormModal.module.less @@ -0,0 +1,130 @@ +.bannerFormModalMain { + .form { + :global { + .ant-form-item { + margin-bottom: 16px; + } + .ant-form-item-explain-error { + font-size: 12px; + } + } + } + + .uploader { + position: relative; + } + + .uploadCard { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 140px; + border: 1px dashed #d9d9d9; + border-radius: 6px; + background-color: #fafafa; + cursor: pointer; + transition: border-color 0.2s; + gap: 4px; + + &:hover { + border-color: #1677ff; + } + } + + .uploadCardError { + border-color: #ff4d4f; + } + + .uploadIcon { + font-size: 24px; + color: #999; + font-weight: 300; + line-height: 1; + } + + .uploadHint { + color: #666; + font-size: 14px; + } + + .uploadTip { + color: #999; + font-size: 12px; + text-align: center; + line-height: 1.6; + } + + .previewWrapper { + position: relative; + width: 100%; + height: 140px; + border: 1px solid #d9d9d9; + border-radius: 6px; + overflow: hidden; + background-color: #f5f5f5; + + :global { + .ant-spin-nested-loading, + .ant-spin-container { + width: 100%; + height: 100%; + } + } + } + + .previewImg { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + } + + .uploadAgainBtn { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.5); + color: #fff; + text-align: center; + padding: 4px 0; + cursor: pointer; + font-size: 12px; + user-select: none; + transition: background 0.2s; + + &:hover { + background: rgba(0, 0, 0, 0.65); + } + } + + .tip { + padding: 8px 12px; + margin-bottom: 16px; + background-color: #f0f5ff; + border: 1px solid #d6e4ff; + border-radius: 4px; + color: #1677ff; + font-size: 13px; + } + + .timeRow { + display: flex; + gap: 16px; + + .timeItem { + flex: 1; + } + } + + .footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding-top: 16px; + border-top: 1px solid #f0f0f0; + margin-top: 8px; + } +} diff --git a/src/pages/events/banner/components/BannerFormModal.tsx b/src/pages/events/banner/components/BannerFormModal.tsx new file mode 100644 index 0000000..e662968 --- /dev/null +++ b/src/pages/events/banner/components/BannerFormModal.tsx @@ -0,0 +1,742 @@ +import { defineComponent, ref, reactive, computed, nextTick } from 'vue'; +import { useEffect, useState } from '@/hooks'; +import { + Modal, + Form, + Input, + Select, + DatePicker, + Cascader, + InputNumber, + Button, + message, + Spin, +} from 'ant-design-vue'; +import { JUMP_TYPE_OPTIONS } from '../model/useBannerModel'; +import { + TITLE_MAX, + PATH_MAX, + URL_MAX, + titleRules, + imageUrlRules, + typeRules, + linkUrlRules, + miniProgramPathRules, + areaRules, + startTimeRules, + endTimeRules, + sortRules, +} from '../model/form'; +import { getProvinceCityCascaderOptions, cityNameToCascaderPath } from '@/utils/areaData'; +import { ImageCropper } from '@yp-component/root'; +import { FileImageOutlined } from '@ant-design/icons-vue'; +import { uploadFile, OssUploadType } from '@/utils/oss'; +import { isImageFile } from '@/utils/form'; +import { getEventList, getEventDetail, type TournamentAdminVO } from '../../list/model/services'; +import dayjs from 'dayjs'; +import styles from './BannerFormModal.module.less'; + +/** 所属区域 Cascader 选项:全国 + 省市两级 */ +const areaCascaderOptions: any[] = [ + { value: '全国', label: '全国' }, + ...getProvinceCityCascaderOptions(), +]; + +/** 默认表单数据 */ +const getDefaultForm = () => ({ + title: '', + imageUrl: '', + imageStatus: 'empty' as 'empty' | 'uploading' | 'success' | 'error', + type: '1' as '1' | '2' | '3' | '0', + linkUrl: '', + relatedEventId: undefined as string | undefined, + area: '全国', + displayTimeRange: null as [string, string] | null, + sort: 1, +}); + +/** 图片上传限制:最大 10MB */ +const IMAGE_MAX_SIZE = 10 * 1024 * 1024; + +interface BannerFormModalProps { + visible: boolean; + record: any; + submitting?: boolean; + onClose: () => void; + onSave: (formData: any) => void; +} + +/** + * 将 base64 字符串转换为 File 对象 + */ +function base64ToFile(dataUrl: string, fileName: string): File { + const arr = dataUrl.split(','); + const mime = arr[0].match(/:(.*?);/)?.[1] || 'image/png'; + const bstr = atob(arr[1]); + const u8arr = new Uint8Array(bstr.length); + for (let i = 0; i < bstr.length; i++) { + u8arr[i] = bstr.charCodeAt(i); + } + return new File([u8arr], fileName, { type: mime }); +} + +// ============================================================ +// 工具函数 +// ============================================================ + +/** 从 Cascader 路径中提取城市名(最后一级),用于区域筛选 */ +function extractCityName(path: string[]): string { + if (!path || path.length === 0 || path[0] === '全国') return ''; + return path[path.length - 1]; +} + +/** 从 Cascader 路径中提取存储用的区域字符串 */ +function extractAreaValue(path: string[]): string { + if (!path || path.length === 0) return '全国'; + if (path[0] === '全国') return '全国'; + // 直辖市路径如 ["北京市"],普通城市如 ["广东省", "深圳市"] + return path[path.length - 1]; +} + +/** 根据跳转类型计算 parms 字段的值 */ +function buildParams(type: string, relatedEventId: string | undefined, linkUrl: string): string { + if (type === '1') return relatedEventId ?? ''; + if (type === '2') return linkUrl || ''; + if (type === '3') return linkUrl || ''; + // type === '0' 无链接 + return ''; +} + +// ============================================================ +// 组件 +// ============================================================ + +/** + * Banner 新增/编辑表单弹窗 + */ +export default defineComponent({ + name: 'BannerFormModal', + props: { + visible: { type: Boolean, default: false }, + record: { type: Object, default: null }, + submitting: { type: Boolean, default: false }, + onClose: { type: Function, required: true }, + onSave: { type: Function, required: true }, + }, + setup(props: BannerFormModalProps) { + const formRef = ref(); + const fileInputRef = ref(null); + const formData = reactive(getDefaultForm()); + + /** Cascader 区域的路径数组:如 ["全国"]、["广东省", "深圳市"] */ + const areaPath = ref(['全国']); + + /** 是否由用户主动切换了跳转类型 */ + const typeChanged = ref(false); + + // ---- 裁剪弹窗状态 ---- + const [cropperVisible, setCropperVisible] = useState(false); + const [uploadImageUrl, setUploadImageUrl] = useState(''); + + // ---- 赛事搜索状态 ---- + const eventKeyword = ref(''); + const [eventOptions, setEventOptions] = useState<{ value: string; label: string }[]>([]); + const [eventSearchLoading, setEventSearchLoading] = useState(false); + /** 编辑时加载赛事详情的独立 loading */ + const [editLoading, setEditLoading] = useState(false); + /** 手动防抖计时器 */ + let searchTimer: ReturnType | null = null; + + /** 赛事下拉选项:第一项固定为"不关联" */ + const eventSelectOptions = computed(() => { + return [{ value: '', label: '不关联' }, ...eventOptions.value]; + }); + + // ---- 赛事搜索 API 调用 ---- + const doSearch = async (keyword: string) => { + if (!keyword.trim()) { + setEventOptions([]); + return; + } + setEventSearchLoading(true); + try { + const cityName = extractCityName(areaPath.value); + const params: Record = { + page: '1', + limit: '999', + name: keyword.trim(), + }; + if (cityName) { + params.areaName = cityName; + } + const res = await getEventList(params as any); + if (res.code == 200) { + const list: TournamentAdminVO[] = res.data?.list || []; + setEventOptions( + list.map((item) => ({ + value: item.id, + label: item.name, + })), + ); + } else { + setEventOptions([]); + } + } catch (e) { + console.error('[Banner] 搜索赛事失败:', e); + setEventOptions([]); + } finally { + setEventSearchLoading(false); + } + }; + + /** 赛事搜索输入(带 350ms 手动防抖) */ + const onEventSearch = (value: string) => { + eventKeyword.value = value; + if (searchTimer) clearTimeout(searchTimer); + if (!value.trim()) { + setEventOptions([]); + return; + } + searchTimer = setTimeout(() => { + doSearch(value); + }, 350); + }; + + /** 所属区域 Cascader 变化 */ + const onAreaChange = (val: any) => { + const path: string[] = val || []; + const newArea = extractAreaValue(path); + areaPath.value = path; + formData.area = newArea; + + // 切换到非"全国"区域时清空已关联赛事 + if (newArea !== '全国') { + formData.relatedEventId = undefined; + formRef.value?.clearValidate('relatedEventId'); + } + + // 区域变化时重新搜索(如果当前有关键词) + if (eventKeyword.value.trim()) { + if (searchTimer) clearTimeout(searchTimer); + searchTimer = setTimeout(() => { + doSearch(eventKeyword.value); + }, 0); + } + }; + + // ---- 根据 record 初始化表单 ---- + const initFormFromRecord = (record: any) => { + const fresh = getDefaultForm(); + if (!record) { + Object.assign(formData, fresh); + areaPath.value = ['全国']; + eventKeyword.value = ''; + setEventOptions([]); + return; + } + + // 区域:兼容旧格式 "广东省-深圳市"、新格式 "深圳市"、"全国" + const rawArea = record.area || record.region || ''; + let cityName: string; + if (rawArea.includes('-')) { + cityName = rawArea.split('-').pop() || '全国'; + } else { + cityName = rawArea || '全国'; + } + areaPath.value = cityNameToCascaderPath(cityName); + + // 跳转类型 + const type = String(record.type || record.jumpType || '1'); + + // 关联赛事ID(编辑时从 params/linkUrl 读取) + let relatedEventId: string | undefined; + if (type === '1') { + const eventId = record.params || record.linkUrl || record.jumpUrl || ''; + if (eventId) { + relatedEventId = eventId; + // 异步获取赛事名称用于回显 + setEditLoading(true); + getEventDetail(eventId) + .then((detailRes) => { + if (detailRes.code == 200) { + const eventName = detailRes.data?.name || eventId; + setEventOptions([{ value: eventId, label: eventName }]); + } + }) + .catch(() => { + setEventOptions([{ value: eventId, label: eventId }]); + }) + .finally(() => { + setEditLoading(false); + }); + } + } + + eventKeyword.value = ''; + if (type !== '1' || !relatedEventId) { + setEventOptions([]); + } + + Object.assign(formData, { + ...fresh, + title: record.title || '', + imageUrl: record.img || record.imageUrl || '', + imageStatus: record.img || record.imageUrl ? ('success' as const) : ('empty' as const), + type, + linkUrl: record.params || record.linkUrl || record.jumpUrl || '', + relatedEventId, + area: cityName, + displayTimeRange: + (record.showTimeBegin || record.displayStartTime) && + (record.showTimeEnd || record.displayEndTime) + ? ([ + record.showTimeBegin || record.displayStartTime, + record.showTimeEnd || record.displayEndTime, + ] as [string, string]) + : null, + sort: Number(record.sort ?? 1), + }); + }; + + // ---- 监听 visible 变化重置表单 ---- + useEffect(() => { + if (props.visible) { + typeChanged.value = false; + initFormFromRecord(props.record); + setCropperVisible(false); + setUploadImageUrl(''); + nextTick().then(() => formRef.value?.clearValidate()); + } + }, [() => props.visible]); + + // ---- 清理防抖计时器 ---- + useEffect(() => { + if (!props.visible && searchTimer) { + clearTimeout(searchTimer); + searchTimer = null; + } + }, [() => props.visible]); + + // ---- 监听 type 变化,清理联动字段 ---- + useEffect(() => { + const syncTypeFields = async () => { + if (!typeChanged.value) { + if (formData.type !== '1') formData.relatedEventId = undefined; + if (formData.type !== '2' && formData.type !== '3') formData.linkUrl = ''; + typeChanged.value = true; + return; + } + + if (formData.type !== '1') { + formData.relatedEventId = undefined; + formRef.value?.clearValidate('relatedEventId'); + } + if (formData.type !== '2' && formData.type !== '3') { + formData.linkUrl = ''; + } + const activeNames: string[] = []; + if (formData.type === '1') activeNames.push('relatedEventId'); + if (formData.type === '2' || formData.type === '3') activeNames.push('linkUrl'); + formRef.value?.clearValidate(['relatedEventId', 'linkUrl']); + if (activeNames.length) { + await formRef.value?.validateFields(activeNames).catch(() => {}); + } + }; + + syncTypeFields(); + }, [() => formData.type]); + + /** 重新校验 Banner 图片字段,上传完成后清除或更新错误提示 */ + const revalidateImageUrl = async () => { + await formRef.value?.validateFields(['imageUrl']).catch(() => {}); + }; + + // ---- 图片上传相关 ---- + const triggerFilePicker = () => { + if (formData.imageStatus === 'uploading') return; + fileInputRef.value?.click(); + }; + + const handleFileChange = (e: Event) => { + const target = e.target as HTMLInputElement; + const file = target.files?.[0]; + target.value = ''; + + if (!file) return; + if (!isImageFile(file)) { + message.error('请选择图片文件'); + return; + } + if (file.size > IMAGE_MAX_SIZE) { + message.error(`图片大小不能超过 ${IMAGE_MAX_SIZE / 1024 / 1024}MB`); + return; + } + + setUploadImageUrl(URL.createObjectURL(file)); + setCropperVisible(true); + }; + + const handleCropConfirm = async (dataUrl: string) => { + setCropperVisible(false); + + if (formData.imageUrl && formData.imageUrl.startsWith('blob:')) { + URL.revokeObjectURL(formData.imageUrl); + } + + const localUrl = dataUrl; + formData.imageUrl = localUrl; + formData.imageStatus = 'uploading'; + + try { + const file = base64ToFile(dataUrl, `banner_${Date.now()}.jpg`); + const result = await uploadFile(file, OssUploadType.EventCover); + + URL.revokeObjectURL(localUrl); + formData.imageUrl = result.url; + formData.imageStatus = 'success'; + message.success('图片上传成功'); + revalidateImageUrl(); + } catch (err: any) { + console.error('[Banner] 上传失败:', err); + URL.revokeObjectURL(localUrl); + formData.imageUrl = ''; + formData.imageStatus = 'error'; + message.error(err?.message || '图片上传失败'); + revalidateImageUrl(); + } + + if (uploadImageUrl.value) { + URL.revokeObjectURL(uploadImageUrl.value); + } + setUploadImageUrl(''); + }; + + const onCropperCancel = () => { + setCropperVisible(false); + if (uploadImageUrl.value) { + URL.revokeObjectURL(uploadImageUrl.value); + } + setUploadImageUrl(''); + }; + + const handleRemoveImage = () => { + if (formData.imageUrl && formData.imageUrl.startsWith('blob:')) { + URL.revokeObjectURL(formData.imageUrl); + } + formData.imageUrl = ''; + formData.imageStatus = 'empty'; + revalidateImageUrl(); + }; + + // ---- 提交表单 ---- + const handleSubmit = async () => { + try { + await formRef.value?.validate(); + } catch { + return; + } + + if (formData.imageStatus === 'uploading') { + message.warning('图片仍在上传中,请稍候'); + return; + } + + const payload = { + img: formData.imageUrl || '', + title: formData.title || '', + type: formData.type, + params: buildParams(formData.type, formData.relatedEventId, formData.linkUrl), + area: formData.area, + showTimeBegin: formData.displayTimeRange?.[0] || '', + showTimeEnd: formData.displayTimeRange?.[1] || '', + sort: String(formData.sort ?? 1), + }; + props.onSave(payload); + }; + + return () => ( + <> + + {editLoading.value ? ( +
+ +
+ ) : ( + <> +
+ {/* ===== Banner 标题 ===== */} + + { + formData.title = val; + }} + placeholder="请输入标题" + maxlength={TITLE_MAX} + allowClear + /> + + + {/* ===== 上传图片(必填,带裁剪) ===== */} + formData.imageStatus, + () => formData.imageUrl, + )} + > +
+ + {formData.imageStatus === 'empty' || formData.imageStatus === 'error' ? ( +
+ + + + 点击上传 + + 建议尺寸 1053*351 像素 +
+ 支持 JPG/PNG,最大 10MB +
+
+ ) : ( +
+ + banner preview + +
+ 重新上传 +
+
+ )} +
+
+ + {/* ===== 跳转类型 ===== */} + + { + formData.relatedEventId = val; + }} + showSearch + filterOption={false} + allowClear={!eventSearchLoading.value} + loading={eventSearchLoading.value} + placeholder="请输入关键字搜索目标赛事" + options={eventSelectOptions.value as any} + onSearch={onEventSearch} + onFocus={() => { + if (formData.relatedEventId && eventOptions.value.length === 0) { + doSearch(formData.relatedEventId); + } + }} + /> + + )} + + {/* ===== 联动字段:自定义链接 ===== */} + {formData.type === '2' && ( + + { + formData.linkUrl = val; + }} + placeholder="https://xxx" + maxlength={URL_MAX} + allowClear + /> + + )} + + {/* ===== 联动字段:小程序页面 ===== */} + {formData.type === '3' && ( + + { + formData.linkUrl = val; + }} + placeholder="/pages/index/index" + maxlength={PATH_MAX} + allowClear + /> + + )} + + {/* ===== 所属区域(省市树种选择 + 全国) ===== */} + + + + + {/* ===== 展示时间(开始/结束) ===== */} +
+ formData.displayTimeRange)} + > + { + const newStart = v ? v.format('YYYY-MM-DD HH:mm:ss') : ''; + const currentEnd = formData.displayTimeRange?.[1] || ''; + if (newStart && currentEnd && dayjs(newStart).isAfter(dayjs(currentEnd))) { + formData.displayTimeRange = [currentEnd, newStart]; + } else { + formData.displayTimeRange = [newStart, currentEnd]; + } + formRef.value?.clearValidate(['displayStartTime', 'displayEndTime']); + }} + style={{ width: '100%' }} + /> + + + formData.displayTimeRange)} + > + { + const newEnd = v ? v.format('YYYY-MM-DD HH:mm:ss') : ''; + const currentStart = formData.displayTimeRange?.[0] || ''; + if (newEnd && currentStart && dayjs(newEnd).isBefore(dayjs(currentStart))) { + formData.displayTimeRange = [newEnd, currentStart]; + } else { + formData.displayTimeRange = [currentStart, newEnd]; + } + formRef.value?.clearValidate(['displayStartTime', 'displayEndTime']); + }} + style={{ width: '100%' }} + /> + +
+ + {/* ===== 排序 ===== */} + + { + formData.sort = val; + }} + placeholder="数字越小越靠前" + min={1} + max={999} + precision={0} + style={{ width: '200px' }} + /> + + + + {/* ===== 底部按钮 ===== */} +
+ + +
+ + )} +
+ + {/* ===== 图片裁剪弹窗 ===== */} + {cropperVisible.value && ( + +
+ +
+
+ )} + + ); + }, +}); diff --git a/src/pages/events/banner/index.tsx b/src/pages/events/banner/index.tsx new file mode 100644 index 0000000..1e7f042 --- /dev/null +++ b/src/pages/events/banner/index.tsx @@ -0,0 +1,284 @@ +import { defineComponent } from 'vue'; +import { + Button, + Input, + Table, + Form, + Space, + Select, + Tooltip, + Pagination, + Modal, + Image, +} from 'ant-design-vue'; +import { useBannerModel, BANNER_STATUS_OPTIONS } from './model/useBannerModel'; +import { useContainerSize } from '@/hooks'; +import { usePermissionStore } from '@/stores/permissionStore'; +import BannerFormModal from './components/BannerFormModal'; +import pageStyles from '@/assets/styles/pageLayout.module.less'; + +/** + * bodyCell 渲染函数 + */ +function renderBodyCell({ + column, + text, + record, + renderJumpType, + renderStatus, + onEdit, + onToggleStatus, + onDelete, + permissions, +}: { + column: any; + text: any; + record: any; + renderJumpType: (type: string) => any; + renderStatus: (status: string) => any; + onEdit: (record: any) => void; + onToggleStatus: (record: any) => void; + onDelete: (record: any) => void; + permissions: { + canEdit: boolean; + canToggleStatus: boolean; + canDelete: boolean; + }; +}) { + // 图片列 + if (column.key === 'image') { + return ( + + ); + } + + // 标题列 + if (column.dataIndex === 'title') { + const maxLen = 18; + const raw = text || ''; + const display = raw.length > maxLen ? raw.slice(0, maxLen) + '...' : raw; + return raw.length > maxLen ? ( + + {display} + + ) : ( + {display} + ); + } + + // 跳转类型列 + if (column.dataIndex === 'type') { + return renderJumpType(text); + } + + // 跳转地址列 + if (column.dataIndex === 'linkUrl') { + return {text || '--'}; + } + + // 展示时间列 + if (column.key === 'displayTime') { + return ( + + {record.showTimeBegin} 至 {record.showTimeEnd} + + ); + } + + // 状态列 + if (column.dataIndex === 'status') { + return renderStatus(text); + } + + // 操作列 + if (column.key === 'action') { + const isEnabled = record.status === '1'; + const { canEdit, canToggleStatus, canDelete } = permissions; + if (!canEdit && !canToggleStatus && !canDelete) return --; + + return ( + + {canEdit && ( + + )} + {canToggleStatus && ( + + )} + {canDelete && ( + + )} + + ); + } + + return; +} + +export default defineComponent({ + name: 'EventBanner', + setup() { + const { + filterForm, + loading, + dataSource, + columns, + pagination, + modalVisible, + editingRecord, + formSubmitting, + renderJumpType, + renderStatus, + handleSearch, + handleReset, + handlePageChange, + handleAdd, + handleEdit, + handleCloseModal, + handleSave, + handleDelete, + handleToggleStatus, + } = useBannerModel(); + + const { containerRef, height } = useContainerSize(); + const { hasPermission } = usePermissionStore(); + const canAdd = hasPermission('events.banner.add'); + const operationPermissions = { + canEdit: hasPermission('events.banner.edit'), + canToggleStatus: hasPermission('events.banner.toggle_status'), + canDelete: hasPermission('events.banner.delete'), + }; + + const confirmDelete = (record: any) => { + Modal.confirm({ + title: '删除确认', + content: `确认要删除 "${record.title}" 吗?`, + okText: '确认', + cancelText: '取消', + okButtonProps: { danger: true }, + onOk: () => handleDelete(record), + }); + }; + + const confirmToggleStatus = (record: any) => { + const isEnabled = record.status === '1'; + const actionText = isEnabled ? '禁用' : '启用'; + Modal.confirm({ + title: `${actionText}确认`, + content: `确认要${actionText} "${record.title}" 吗?`, + okText: '确认', + cancelText: '取消', + onOk: () => handleToggleStatus(record), + }); + }; + + const tableColumns = [ + ...columns, + { + title: '操作', + key: 'action', + width: 220, + fixed: 'right' as const, + align: 'center' as const, + }, + ]; + + return () => ( +
+
+
+ + (filterForm.title = val.target?.value ?? val ?? '')} + onPressEnter={handleSearch} + /> + + +
+ {{ + bodyCell: (args: any) => + renderBodyCell({ + ...args, + renderJumpType, + renderStatus, + onEdit: handleEdit, + onToggleStatus: confirmToggleStatus, + onDelete: confirmDelete, + permissions: operationPermissions, + }), + }} +
+
+ +
+ `共 ${total} 条`} + onChange={handlePageChange} + /> +
+
+ + {modalVisible.value && ( + + )} +
+ ); + }, +}); diff --git a/src/pages/events/banner/model/form.ts b/src/pages/events/banner/model/form.ts new file mode 100644 index 0000000..49b808e --- /dev/null +++ b/src/pages/events/banner/model/form.ts @@ -0,0 +1,114 @@ +/** + * Banner 表单域控制器 + * + * 集中管理 Banner 新增/编辑弹窗的所有表单规则校验与常量。 + * 各规则数组 / 工厂函数可直接传给 antd Form.Item 的 rules prop。 + */ +import type { Rule } from 'ant-design-vue/es/form'; + +// ============================================================ +// 常量 +// ============================================================ + +/** 标题最大长度 */ +export const TITLE_MAX = 50; +/** 小程序路径最大长度 */ +export const PATH_MAX = 200; +/** 跳转链接最大长度 */ +export const URL_MAX = 500; + +// ============================================================ +// 表单规则 +// ============================================================ + +/** Banner 标题:必填 + 50 字符上限 */ +export const titleRules: Rule[] = [ + { required: true, message: '请输入' }, + { max: TITLE_MAX, message: `标题最多 ${TITLE_MAX} 个字符` }, +]; + +/** + * Banner 图片:必填 + 上传状态校验 + * + * @param getImageStatus 获取当前上传状态的 getter + * @param getImageUrl 获取当前图片 URL 的 getter + */ +export function imageUrlRules(getImageStatus: () => string, getImageUrl: () => string): Rule[] { + return [ + { required: true, message: '请上传 Banner 图片' }, + { + validator: async () => { + if (getImageStatus() === 'uploading') { + return Promise.reject(new Error('图片仍在上传中,请稍候')); + } + const url = getImageUrl(); + if (url.startsWith('blob:') || url.startsWith('data:')) { + return Promise.reject(new Error('请等待图片上传完成')); + } + return Promise.resolve(); + }, + }, + ]; +} + +/** 跳转类型:必选 */ +export const typeRules: Rule[] = [{ required: true, message: '请选择跳转类型' }]; + +/** 跳转地址(自定义链接 scene=2):必填 + 最大长度 */ +export const linkUrlRules: Rule[] = [ + { required: true, message: '请输入跳转地址' }, + { max: URL_MAX, message: `链接最多 ${URL_MAX} 个字符` }, +]; + +/** 小程序页面路径(scene=3):必填 + 最大长度 */ +export const miniProgramPathRules: Rule[] = [ + { required: true, message: '请输入小程序页面路径' }, + { max: PATH_MAX, message: `路径最多 ${PATH_MAX} 个字符` }, +]; + +/** 所属区域:必选 */ +export const areaRules: Rule[] = [{ required: true, message: '请选择所属区域' }]; + +/** 排序:必填 + 1-999 整数 */ +export const sortRules: Rule[] = [ + { required: true, message: '请输入排序' }, + { type: 'number', min: 1, max: 999, message: '排序范围 1-999' }, +]; + +/** + * 展示开始时间规则:校验 displayTimeRange[0] 非空 + * + * @param getDisplayTimeRange 获取展示时间范围的 getter + */ +export function startTimeRules(getDisplayTimeRange: () => [string, string] | null): Rule[] { + return [ + { + validator: async () => { + const range = getDisplayTimeRange(); + if (!range?.[0]) { + return Promise.reject(new Error('请选择开始时间')); + } + return Promise.resolve(); + }, + }, + ]; +} + +/** + * 展示结束时间规则:校验 displayTimeRange[1] 非空 + * + * @param getDisplayTimeRange 获取展示时间范围的 getter + */ +export function endTimeRules(getDisplayTimeRange: () => [string, string] | null): Rule[] { + return [ + { + validator: async () => { + const range = getDisplayTimeRange(); + if (!range?.[1]) { + return Promise.reject(new Error('请选择结束时间')); + } + return Promise.resolve(); + }, + }, + ]; +} diff --git a/src/pages/events/banner/model/services.ts b/src/pages/events/banner/model/services.ts new file mode 100644 index 0000000..0c3f54d --- /dev/null +++ b/src/pages/events/banner/model/services.ts @@ -0,0 +1,64 @@ +/** + * Banner 管理 - 接口服务 + * 该页面使用的所有接口集中管理 + */ +import { get, post } from '@/utils/request'; +import type { + BannerListQueryParams, + BannerSaveParams, + BannerDelParams, + BannerActiveParams, + TournamentAdminBannerListVO, + ApiResult, +} from '@/api/banner/types'; + +export type { + TournamentAdminBannerListVO, + BannerListQueryParams, + BannerSaveParams, + BannerDelParams, + BannerActiveParams, + ApiResult, +} from '@/api/banner/types'; + +// ============================================================ +// URL 常量 +// ============================================================ +const bannerList = '/admin/manager/banner/list'; +const bannerDel = '/admin/manager/banner/del'; +const bannerSave = '/admin/manager/banner/save'; +const bannerUpdate = '/admin/manager/banner/update'; +const bannerActive = '/admin/manager/banner/active'; + +// ============================================================ +// API 函数 +// ============================================================ + +/** Banner 列表 */ +export async function getBannerList( + params: BannerListQueryParams, +): Promise> { + return get(bannerList, params as Record); +} + +/** 删除 Banner */ +export function delBanner(id: string): Promise>> { + return post(bannerDel, { id }); +} + +/** 新增 Banner */ +export function saveBanner(params: BannerSaveParams): Promise>> { + return post(bannerSave, params); +} + +/** 编辑 Banner */ +export function updateBanner(params: BannerSaveParams): Promise>> { + return post(bannerUpdate, params); +} + +/** 启用/禁用 Banner */ +export function toggleBannerActive( + params: BannerActiveParams, +): Promise>> { + return post(bannerActive, params); +} diff --git a/src/pages/events/banner/model/useBannerModel.ts b/src/pages/events/banner/model/useBannerModel.ts new file mode 100644 index 0000000..7adaafc --- /dev/null +++ b/src/pages/events/banner/model/useBannerModel.ts @@ -0,0 +1,282 @@ +import { computed, reactive, toRef, Ref, h } from 'vue'; +import { message } from 'ant-design-vue'; +import { StatusTag, type StatusTagTone } from '@/components'; +import { useState, useDebounce, useThrottleFn } from '@/hooks'; +import { useRequest } from '@/hooks/useRequest'; +import { useEffect } from '@/hooks/useEffect'; +import { usePagination } from '@/hooks/usePagination'; +import { + getBannerList, + delBanner, + saveBanner, + updateBanner, + toggleBannerActive, + type TournamentAdminBannerListVO, + type BannerListQueryParams, +} from './services'; + +// ============================================================ +// 常量 +// ============================================================ + +/** Banner 状态选项(value 对应 API status: 不传=全部、0=禁用;1=启用) */ +export const BANNER_STATUS_OPTIONS = [ + { value: '', label: '全部' }, + { value: '1', label: '启用' }, + { value: '0', label: '禁用' }, +] as const; + +/** 跳转类型选项(value 对应 API type: 0=无链接,1=赛事详情,2=自定义连接,3=小程序页面) */ +export const JUMP_TYPE_OPTIONS = [ + { value: '1', label: '赛事详情页' }, + { value: '2', label: '自定义链接' }, + { value: '3', label: '小程序页面' }, + { value: '0', label: '无链接' }, +] as const; + +/** 跳转类型映射 */ +const JUMP_TYPE_MAP: Record = { + '0': { label: '无链接', tone: 'default' }, + '1': { label: '赛事详情页', tone: 'primary' }, + '2': { label: '自定义链接', tone: 'purple' }, + '3': { label: '小程序页面', tone: 'cyan' }, +}; + +/** Banner 状态映射 */ +const STATUS_MAP: Record = { + '1': { label: '启用中', tone: 'success' }, + '0': { label: '已禁用', tone: 'default', dimmed: true }, +}; + +// ============================================================ +// Model +// ============================================================ + +export function useBannerModel() { + // ===== 筛选条件(key 名对齐 API 查询参数) ===== + const filterForm = reactive({ + title: '', + status: '' as '' | '0' | '1', + }); + + const { debouncedValue: debouncedTitle } = useDebounce( + toRef(filterForm, 'title') as Ref, + { delay: 300 }, + ); + + // ===== 构建 API 查询参数 ===== + const buildQueryParams = (): BannerListQueryParams => { + const params: BannerListQueryParams = {}; + if (filterForm.title.trim()) { + params.title = filterForm.title.trim(); + } + if (filterForm.status) { + params.status = filterForm.status; + } + return params; + }; + + // ===== 数据请求(API 返回全量,前端分页) ===== + const { + data, + loading, + run: fetchList, + } = useRequest(() => getBannerList(buildQueryParams()), { + formatResult: (res) => (res.code == 200 ? res.data : []), + }); + + const allData = computed(() => data.value || []); + + // ===== 前端分页 ===== + const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 }); + + /** 当前页数据(客户端切片) */ + const dataSource = computed(() => { + const start = ((pagination as any).current.value - 1) * (pagination as any).pageSize.value; + return allData.value.slice(start, start + (pagination as any).pageSize.value); + }); + + // 同步前端分页 total + useEffect(() => { + pagination.setTotal(allData.value.length); + }, [allData]); + + // ===== 弹窗状态 ===== + const [modalVisible, setModalVisible] = useState(false); + const [editingRecord, setEditingRecord] = useState(null); + const [formSubmitting, setFormSubmitting] = useState(false); + + // ===== 表格列配置(dataIndex 对齐 TournamentAdminBannerListVO) ===== + const columns = [ + { title: '图片', key: 'image', width: 100, align: 'center' as const }, + { title: '标题', dataIndex: 'title', key: 'title', width: 220 }, + { + title: '跳转类型', + dataIndex: 'type', + key: 'type', + width: 120, + }, + { title: '所属区域', dataIndex: 'area', key: 'area', width: 140 }, + { title: '展示时间', key: 'displayTime', width: 260 }, + { title: '创建时间', dataIndex: 'createDate', key: 'createDate', width: 160 }, + { title: '状态', dataIndex: 'status', key: 'status', width: 100, align: 'center' as const }, + ]; + + // ===== 计算属性 ===== + const hasFilter = computed(() => { + return debouncedTitle.value.trim() !== '' || filterForm.status !== ''; + }); + + const isEdit = computed(() => editingRecord.value !== null); + + // ===== 方法 ===== + + /** 查询 */ + const handleSearch = () => { + pagination.reset(); + fetchList(); + }; + + /** 重置 */ + const handleReset = useThrottleFn(async () => { + filterForm.title = ''; + filterForm.status = ''; + pagination.reset(); + await fetchList(); + }, 500); + + const handlePageChange = (page: number, pageSize: number) => { + pagination.setCurrent(page); + if (pageSize !== (pagination as any).pageSize.value) { + pagination.setPageSize(pageSize); + } + }; + + /** 打开新增弹窗 */ + const handleAdd = () => { + setEditingRecord(null); + setModalVisible(true); + }; + const handleEdit = (record: any) => { + setEditingRecord(record); + setModalVisible(true); + }; + const handleCloseModal = () => { + setModalVisible(false); + setEditingRecord(null); + }; + + /** 提交表单(新增/编辑) */ + const handleSave = useThrottleFn(async (formPayload: any) => { + if (formSubmitting.value) return; + setFormSubmitting(true); + try { + const payload = { + img: formPayload.img || formPayload.imageUrl || '', + title: formPayload.title || '', + type: String(formPayload.type || formPayload.jumpType || '1'), + params: + formPayload.params ?? + formPayload.parms ?? + formPayload.linkUrl ?? + formPayload.jumpUrl ?? + '', + area: formPayload.area || formPayload.region || '', + showTimeBegin: + formPayload.showTimeBegin || + formPayload.displayTimeRange?.[0] || + formPayload.displayStartTime || + '', + showTimeEnd: + formPayload.showTimeEnd || + formPayload.displayTimeRange?.[1] || + formPayload.displayEndTime || + '', + sort: String(formPayload.sort ?? 1), + }; + const isEdit = editingRecord.value !== null; + const res = isEdit + ? await updateBanner({ ...payload, id: editingRecord.value.id }) + : await saveBanner(payload); + if (res.code == 200) { + message.success(isEdit ? '编辑成功' : '新增成功'); + handleCloseModal(); + handleSearch(); + } else { + message.error(res.msg || '操作失败'); + } + } catch (e: any) { + console.error('Banner提交失败:', e); + } finally { + setFormSubmitting(false); + } + }, 500); + + /** 删除 */ + const handleDelete = useThrottleFn(async (record: any) => { + try { + const res = await delBanner(record.id); + if (res.code == 200) { + message.success('删除成功'); + fetchList(); + } else { + message.error(res.msg || '删除失败'); + } + } catch (e: any) { + console.error('Banner删除失败:', e); + } + }, 500); + + /** 启用/禁用切换 */ + const handleToggleStatus = useThrottleFn(async (record: any) => { + try { + const newStatus = record.status === '1' ? '0' : '1'; + const actionText = newStatus === '1' ? '启用' : '禁用'; + const res = await toggleBannerActive({ id: record.id, status: newStatus }); + if (res.code == 200) { + message.success(`${actionText}成功`); + fetchList(); + } else { + message.error(res.msg || '操作失败'); + } + } catch (e: any) { + console.error('Banner状态切换失败:', e); + } + }, 500); + + // ===== 渲染工具 ===== + const renderJumpType = (type: string) => { + const info = JUMP_TYPE_MAP[type] || { label: type || '--', tone: 'default' as const }; + return h(StatusTag, { label: info.label, tone: info.tone }); + }; + + const renderStatus = (status: string) => { + const info = STATUS_MAP[status] || { label: status || '--', tone: 'default' as const }; + return h(StatusTag, { label: info.label, tone: info.tone, dimmed: info.dimmed }); + }; + + return { + filterForm, + loading, + dataSource, + columns, + pagination, + hasFilter, + modalVisible, + setModalVisible, + editingRecord, + isEdit, + formSubmitting, + renderJumpType, + renderStatus, + handleSearch, + handleReset, + handlePageChange, + handleAdd, + handleEdit, + handleCloseModal, + handleSave, + handleDelete, + handleToggleStatus, + }; +} diff --git a/src/pages/events/list/components/EventAuditModal.module.less b/src/pages/events/list/components/EventAuditModal.module.less new file mode 100644 index 0000000..cc88632 --- /dev/null +++ b/src/pages/events/list/components/EventAuditModal.module.less @@ -0,0 +1,61 @@ +.eventAuditModalMain { + .section { + margin-bottom: 24px; + + &:last-child { + margin-bottom: 0; + } + } + + .sectionTitle { + font-size: 14px; + font-weight: 600; + color: rgba(0, 0, 0, 0.85); + margin-bottom: 12px; + padding-left: 8px; + border-left: 3px solid #1677ff; + } + + .desc { + :global(.ant-descriptions-item-label) { + width: 110px; + color: rgba(0, 0, 0, 0.65); + } + } + + .auditSection { + margin-top: 24px; + } + + .auditHeader { + display: flex; + align-items: center; + margin-bottom: 16px; + } + + .auditTitle { + font-size: 14px; + font-weight: 600; + color: rgba(0, 0, 0, 0.85); + padding-left: 8px; + border-left: 3px solid #1677ff; + } + + .auditForm { + border-top: 1px solid #f0f0f0; + padding-top: 16px; + } + + .auditField { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + + :global(.ant-form-item-label) { + font-weight: 500; + color: rgba(0, 0, 0, 0.65); + } + } +} diff --git a/src/pages/events/list/components/EventAuditModal.tsx b/src/pages/events/list/components/EventAuditModal.tsx new file mode 100644 index 0000000..892f4d8 --- /dev/null +++ b/src/pages/events/list/components/EventAuditModal.tsx @@ -0,0 +1,201 @@ +import { defineComponent, reactive, ref } from 'vue'; +import { Modal, Descriptions, Image, Radio, Input, Form, Spin, message } from 'ant-design-vue'; +import { useEffect, useState } from '@/hooks'; +import { getEventDetail, type TournamentAdminInfoVO } from '../model/services'; +import { validateEventAuditSubmit } from '../model/form'; +import styles from './EventAuditModal.module.less'; + +const { TextArea } = Input; + +interface EventAuditModalProps { + visible: boolean; + record: any; + onClose: () => void; + onSubmit?: (payload: { approved: boolean; comment: string }) => void; +} + +/** + * 赛事审核弹窗 + * 包含:基础信息 + 封面 + 审核操作(通过/未通过 + 审核内容) + */ +export default defineComponent({ + name: 'EventAuditModal', + props: { + visible: { type: Boolean, default: false }, + record: { type: Object, default: () => ({}) }, + onClose: { type: Function, required: true }, + onSubmit: { type: Function, default: undefined }, + }, + setup(props: EventAuditModalProps) { + const detail = ref(null); + const [loading, setLoading] = useState(false); + + // 审核表单 + const auditForm = reactive({ + approved: true, + comment: '', + }); + + /** 加载赛事详情 */ + const fetchDetail = async (id: string) => { + setLoading(true); + detail.value = null; + try { + const res = await getEventDetail(id); + if (res.code == 200) { + detail.value = res.data; + } else { + message.error(res.msg || '获取赛事详情失败'); + } + } catch { + // 网络层已统一提示 + } finally { + setLoading(false); + } + }; + + /** 弹窗打开时重新拉取详情 */ + useEffect(() => { + if (props.visible && props.record?.id) { + auditForm.approved = true; + auditForm.comment = ''; + fetchDetail(props.record.id); + } + }, [() => props.visible]); + + const handleOk = () => { + const check = validateEventAuditSubmit({ + approved: auditForm.approved, + comment: auditForm.comment, + }); + if (!check.valid) { + message.warning(check.error!); + return; + } + if (props.onSubmit) { + props.onSubmit({ approved: auditForm.approved, comment: auditForm.comment }); + } + }; + + /** 格式化时间范围 */ + const formatRange = (start: string, end: string) => { + if (!start && !end) return '--'; + return `${start || '?'} ~ ${end || '?'}`; + }; + + /** 渲染 0/1 开关文本 */ + const boolText = (val: string, trueText: string, falseText: string) => { + return val === '1' ? trueText : falseText; + }; + + /** 渲染详情内容 */ + const renderContent = () => { + const d = detail.value!; + + return ( + <> + {/* ===== 基础信息 ===== */} +
+
基础信息
+ + {d.name || '--'} + + {formatRange(d.startTimeBegin, d.startTimeEnd)} + + + + {d.descr || '--'} + + + + {formatRange(d.signupTimeBegin, d.signupTimeEnd)} + + + {d.signupTimeEnd || '--'} + + + {d.venueArea || '--'} + {d.venue || '--'} + {d.venueNo || '--'} + + + {boolText(d.isPublic, '在首页展示赛事', '非公开')} + + + {boolText(d.isRequireIdCardNo, '需要提供', '无需提供')} + + + {boolText(d.isRequireIdCardImg, '需要提供', '无需提供')} + + + {d.createTime || '--'} + {d.createNickname || '--'} + 待审核 + {d.signupCount || '0'} + + {d.dropoutCount || '0'} + + + {d.totalLimit || '--'} + {d.matchTotalCount || '0'} + {d.matchCount || '0'} + +
+ + {/* ===== 封面 ===== */} +
+
封面
+ {d.img ? : 暂无封面} +
+ + {/* ===== 审核操作 ===== */} +
+
+ 审核操作 +
+ + + + 通过 + 未通过 + + + +