From b75150dca0424ae0951783d85fae40c263534c03 Mon Sep 17 00:00:00 2001 From: kangwenjing <1138819403@qq.com> Date: Tue, 17 Mar 2026 15:18:07 +0800 Subject: [PATCH] 0317 --- .codex/environments/environment.toml | 11 + .gitignore | 24 + README.md | 73 + eslint.config.js | 23 + index.html | 13 + package-lock.json | 4658 +++++++++++++++++ package.json | 39 + public/vite.svg | 1 + src/App.css | 1 + src/App.tsx | 113 + src/api/appraisal.ts | 127 + src/api/login.ts | 18 + src/api/monitor/cache.ts | 63 + src/api/monitor/job.ts | 64 + src/api/monitor/logininfor.ts | 34 + src/api/monitor/online.ts | 17 + src/api/monitor/operlog.ts | 24 + src/api/monitor/server.ts | 9 + src/api/permission.ts | 37 + src/api/project.ts | 180 + src/api/projectExecution.ts | 27 + src/api/projectScore.ts | 45 + src/api/system/config.ts | 60 + src/api/system/dept.ts | 52 + src/api/system/dict.ts | 115 + src/api/system/menu.ts | 60 + src/api/system/role.ts | 119 + src/api/system/user.ts | 136 + src/api/user.ts | 32 + src/api/worklog.ts | 91 + src/assets/error/404.png | 1 + src/assets/login-background.jpg | 1 + src/assets/pay.png | 1 + src/assets/react.svg | 1 + src/components/Navbar/index.tsx | 45 + src/components/PageBackButton.tsx | 29 + src/components/Permission/index.tsx | 39 + src/components/Sidebar/index.tsx | 283 + src/contexts/PermissionContext.tsx | 334 ++ src/index.css | 9 + src/layout/MainLayout.tsx | 42 + src/layout/layout.css | 10 + src/main.tsx | 11 + src/pages/Home/home.css | 37 + src/pages/Home/index.tsx | 103 + src/pages/Login/index.tsx | 131 + src/pages/Login/login.css | 63 + src/pages/Profile/ResetPassword.tsx | 69 + src/pages/Profile/UserInfo.tsx | 77 + src/pages/Profile/index.tsx | 86 + src/pages/Profile/profile.css | 8 + src/pages/dashboard/ProjectExecutionPage.tsx | 787 +++ src/pages/dashboard/project-execution.css | 488 ++ src/pages/monitor/CacheListPage.tsx | 371 ++ src/pages/monitor/CacheMonitorPage.tsx | 145 + src/pages/monitor/JobMonitorPage.tsx | 585 +++ src/pages/monitor/LoginLogPage.tsx | 372 ++ src/pages/monitor/OnlineUserPage.tsx | 167 + src/pages/monitor/OperationLogPage.tsx | 463 ++ src/pages/monitor/ServerMonitorPage.tsx | 185 + src/pages/monitor/cache-list.css | 7 + src/pages/monitor/cache-monitor.css | 5 + src/pages/monitor/job-monitor.css | 11 + src/pages/monitor/login-log.css | 3 + src/pages/monitor/online-user.css | 3 + src/pages/monitor/operation-log.css | 3 + src/pages/monitor/server-monitor.css | 17 + src/pages/project/DemandManagePage.tsx | 1474 ++++++ src/pages/project/ProjectDetailPage.tsx | 135 + src/pages/project/ProjectPage.tsx | 235 + src/pages/project/project.css | 7 + src/pages/projectBank/ProjectUserPage.tsx | 415 ++ src/pages/projectBank/UserProjectPage.tsx | 529 ++ src/pages/projectBank/UserScoreDetailPage.tsx | 347 ++ src/pages/projectBank/UserScorePage.tsx | 419 ++ src/pages/projectBank/project-user.css | 167 + src/pages/projectBank/user-project.css | 220 + src/pages/projectBank/user-score.css | 455 ++ src/pages/system/ConfigPage.tsx | 320 ++ src/pages/system/DeptPage.tsx | 336 ++ src/pages/system/DictPage.tsx | 327 ++ src/pages/system/MenuPage.tsx | 728 +++ src/pages/system/RolePage.tsx | 716 +++ src/pages/system/UserPage.tsx | 480 ++ src/pages/system/config.css | 7 + src/pages/system/dept.css | 11 + src/pages/system/dict.css | 16 + src/pages/system/menu.css | 216 + src/pages/system/role.css | 23 + src/pages/system/user.css | 22 + .../workAppraisal/AppraisalDashboardPage.tsx | 502 ++ .../workAppraisal/AppraisalDetailPage.tsx | 650 +++ .../workAppraisal/AppraisalManagerPage.tsx | 83 + .../AppraisalManagerUserPage.tsx | 139 + .../AppraisalModuleDetailPage.tsx | 355 ++ src/pages/workAppraisal/ManagerPage.tsx | 213 + src/pages/workAppraisal/ManagerUserPage.tsx | 211 + src/pages/workAppraisal/NormalWorkerPage.tsx | 232 + src/pages/workAppraisal/TaskSetPage.tsx | 1168 +++++ .../workAppraisal/appraisal-dashboard.css | 57 + src/pages/workAppraisal/appraisal-detail.css | 189 + .../workAppraisal/appraisal-module-detail.css | 258 + src/pages/workAppraisal/appraisal.css | 54 + src/pages/workAppraisal/manager.css | 83 + src/pages/workAppraisal/taskSet.css | 321 ++ src/pages/worklog/WorkLogPage.tsx | 1310 +++++ src/pages/worklog/worklog.css | 559 ++ src/routeMapper.ts | 252 + src/themes/macarons.ts | 214 + src/types/api.ts | 295 ++ src/types/js-cookie.d.ts | 18 + src/utils/auth.ts | 16 + src/utils/request.ts | 85 + src/utils/ruoyi.ts | 47 + tsconfig.app.json | 34 + tsconfig.json | 7 + tsconfig.node.json | 26 + vite.config.ts | 23 + 118 files changed, 26264 insertions(+) create mode 100644 .codex/environments/environment.toml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/vite.svg create mode 100644 src/App.css create mode 100644 src/App.tsx create mode 100644 src/api/appraisal.ts create mode 100644 src/api/login.ts create mode 100644 src/api/monitor/cache.ts create mode 100644 src/api/monitor/job.ts create mode 100644 src/api/monitor/logininfor.ts create mode 100644 src/api/monitor/online.ts create mode 100644 src/api/monitor/operlog.ts create mode 100644 src/api/monitor/server.ts create mode 100644 src/api/permission.ts create mode 100644 src/api/project.ts create mode 100644 src/api/projectExecution.ts create mode 100644 src/api/projectScore.ts create mode 100644 src/api/system/config.ts create mode 100644 src/api/system/dept.ts create mode 100644 src/api/system/dict.ts create mode 100644 src/api/system/menu.ts create mode 100644 src/api/system/role.ts create mode 100644 src/api/system/user.ts create mode 100644 src/api/user.ts create mode 100644 src/api/worklog.ts create mode 100644 src/assets/error/404.png create mode 100644 src/assets/login-background.jpg create mode 100644 src/assets/pay.png create mode 100644 src/assets/react.svg create mode 100644 src/components/Navbar/index.tsx create mode 100644 src/components/PageBackButton.tsx create mode 100644 src/components/Permission/index.tsx create mode 100644 src/components/Sidebar/index.tsx create mode 100644 src/contexts/PermissionContext.tsx create mode 100644 src/index.css create mode 100644 src/layout/MainLayout.tsx create mode 100644 src/layout/layout.css create mode 100644 src/main.tsx create mode 100644 src/pages/Home/home.css create mode 100644 src/pages/Home/index.tsx create mode 100644 src/pages/Login/index.tsx create mode 100644 src/pages/Login/login.css create mode 100644 src/pages/Profile/ResetPassword.tsx create mode 100644 src/pages/Profile/UserInfo.tsx create mode 100644 src/pages/Profile/index.tsx create mode 100644 src/pages/Profile/profile.css create mode 100644 src/pages/dashboard/ProjectExecutionPage.tsx create mode 100644 src/pages/dashboard/project-execution.css create mode 100644 src/pages/monitor/CacheListPage.tsx create mode 100644 src/pages/monitor/CacheMonitorPage.tsx create mode 100644 src/pages/monitor/JobMonitorPage.tsx create mode 100644 src/pages/monitor/LoginLogPage.tsx create mode 100644 src/pages/monitor/OnlineUserPage.tsx create mode 100644 src/pages/monitor/OperationLogPage.tsx create mode 100644 src/pages/monitor/ServerMonitorPage.tsx create mode 100644 src/pages/monitor/cache-list.css create mode 100644 src/pages/monitor/cache-monitor.css create mode 100644 src/pages/monitor/job-monitor.css create mode 100644 src/pages/monitor/login-log.css create mode 100644 src/pages/monitor/online-user.css create mode 100644 src/pages/monitor/operation-log.css create mode 100644 src/pages/monitor/server-monitor.css create mode 100644 src/pages/project/DemandManagePage.tsx create mode 100644 src/pages/project/ProjectDetailPage.tsx create mode 100644 src/pages/project/ProjectPage.tsx create mode 100644 src/pages/project/project.css create mode 100644 src/pages/projectBank/ProjectUserPage.tsx create mode 100644 src/pages/projectBank/UserProjectPage.tsx create mode 100644 src/pages/projectBank/UserScoreDetailPage.tsx create mode 100644 src/pages/projectBank/UserScorePage.tsx create mode 100644 src/pages/projectBank/project-user.css create mode 100644 src/pages/projectBank/user-project.css create mode 100644 src/pages/projectBank/user-score.css create mode 100644 src/pages/system/ConfigPage.tsx create mode 100644 src/pages/system/DeptPage.tsx create mode 100644 src/pages/system/DictPage.tsx create mode 100644 src/pages/system/MenuPage.tsx create mode 100644 src/pages/system/RolePage.tsx create mode 100644 src/pages/system/UserPage.tsx create mode 100644 src/pages/system/config.css create mode 100644 src/pages/system/dept.css create mode 100644 src/pages/system/dict.css create mode 100644 src/pages/system/menu.css create mode 100644 src/pages/system/role.css create mode 100644 src/pages/system/user.css create mode 100644 src/pages/workAppraisal/AppraisalDashboardPage.tsx create mode 100644 src/pages/workAppraisal/AppraisalDetailPage.tsx create mode 100644 src/pages/workAppraisal/AppraisalManagerPage.tsx create mode 100644 src/pages/workAppraisal/AppraisalManagerUserPage.tsx create mode 100644 src/pages/workAppraisal/AppraisalModuleDetailPage.tsx create mode 100644 src/pages/workAppraisal/ManagerPage.tsx create mode 100644 src/pages/workAppraisal/ManagerUserPage.tsx create mode 100644 src/pages/workAppraisal/NormalWorkerPage.tsx create mode 100644 src/pages/workAppraisal/TaskSetPage.tsx create mode 100644 src/pages/workAppraisal/appraisal-dashboard.css create mode 100644 src/pages/workAppraisal/appraisal-detail.css create mode 100644 src/pages/workAppraisal/appraisal-module-detail.css create mode 100644 src/pages/workAppraisal/appraisal.css create mode 100644 src/pages/workAppraisal/manager.css create mode 100644 src/pages/workAppraisal/taskSet.css create mode 100644 src/pages/worklog/WorkLogPage.tsx create mode 100644 src/pages/worklog/worklog.css create mode 100644 src/routeMapper.ts create mode 100644 src/themes/macarons.ts create mode 100644 src/types/api.ts create mode 100644 src/types/js-cookie.d.ts create mode 100644 src/utils/auth.ts create mode 100644 src/utils/request.ts create mode 100644 src/utils/ruoyi.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.codex/environments/environment.toml b/.codex/environments/environment.toml new file mode 100644 index 0000000..f3fd73c --- /dev/null +++ b/.codex/environments/environment.toml @@ -0,0 +1,11 @@ +# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY +version = 1 +name = "pms-react-new" + +[setup] +script = "" + +[[actions]] +name = "运行" +icon = "run" +command = "npm run dev" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2e7761 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..cf6de9a --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + pms-react-new + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..36f93eb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4658 @@ +{ + "name": "pms-react-new", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pms-react-new", + "version": "0.0.0", + "dependencies": { + "@types/file-saver": "^2.0.7", + "antd": "^6.3.1", + "axios": "^1.13.5", + "dayjs": "^1.11.19", + "echarts": "^6.0.0", + "echarts-for-react": "^3.0.6", + "file-saver": "^2.0.5", + "js-cookie": "^3.0.5", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } + }, + "node_modules/@ant-design/colors": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz", + "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.0.tgz", + "integrity": "sha512-eZFrPCnrYrF3XtL7qA4L75P0qA3TtZta8H3Yggy7UYFh8gZgu5bSMNF+v4UVCzGxzYmx8ZvPdgOce0BJ6PsW9g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.1.tgz", + "integrity": "sha512-RKxkj5pGFB+FkPJ5NGhoX3DK3xsv0pMltha7Ei1AnY3tILeq38L7tuhaWDPQI/5nlPxOog44wvqpNyyGcUsNMg==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.1.0", + "@babel/runtime": "^7.23.2", + "@rc-component/util": "^1.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", + "license": "MIT", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.1.0.tgz", + "integrity": "sha512-KrWMu1fIg3w/1F2zfn+JlfNDU8dDqILfA5Tg85iqs1lf8ooyGlbkA+TkwfOKKgqpUmAiRY1PTFpuOU2DAIgSUg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz", + "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "clsx": "^2.1.1", + "json2mq": "^0.2.0", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", + "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.3", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.0.tgz", + "integrity": "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/cascader": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.14.0.tgz", + "integrity": "sha512-Ip9356xwZUR2nbW5PRVGif4B/bDve4pLa/N+PGbvBaTnjbvmN4PFMBGQSmlDlzKP1ovxaYMvwF/dI9lXNLT4iQ==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.6.0", + "@rc-component/tree": "~1.2.0", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/checkbox": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz", + "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/collapse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz", + "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.0.tgz", + "integrity": "sha512-o7Vavj7yyfVxFmeynXf0fCHVlC0UTE9al74c6nYuLck+gjuVdQNWSVXR8Efq/mmWFy7891SCOsfaPq6Eqe1s/g==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.1.tgz", + "integrity": "sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/dialog": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.8.4.tgz", + "integrity": "sha512-Ay6PM7phkTkquplG8fWfUGFZ2GTLx9diTl4f0d8Eqxd7W1u1KjE9AQooFQHOHnhZf0Ya3z51+5EKCWHmt/dNEw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.3", + "@rc-component/portal": "^2.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/drawer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz", + "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.1.3", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dropdown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.2.tgz", + "integrity": "sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/@rc-component/form": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.6.2.tgz", + "integrity": "sha512-OgIn2RAoaSBqaIgzJf/X6iflIa9LpTozci1lagLBdURDFhGA370v0+T0tXxOi8YShMjTha531sFhwtnrv+EJaQ==", + "license": "MIT", + "dependencies": { + "@rc-component/async-validator": "^5.1.0", + "@rc-component/util": "^1.6.2", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/image": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.6.0.tgz", + "integrity": "sha512-tSfn2ZE/oP082g4QIOxeehkmgnXB7R+5AFj/lIFr4k7pEuxHBdyGIq9axoCY9qea8NN0DY6p4IB/F07tLqaT5A==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/portal": "^2.1.2", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/input": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.1.2.tgz", + "integrity": "sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/input-number": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz", + "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==", + "license": "MIT", + "dependencies": { + "@rc-component/mini-decimal": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mentions": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.6.0.tgz", + "integrity": "sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.1.0", + "@rc-component/menu": "~1.2.0", + "@rc-component/textarea": "~1.1.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/menu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.2.0.tgz", + "integrity": "sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/motion": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.1.tgz", + "integrity": "sha512-Wo1mkd0tCcHtvYvpPOmlYJz546z16qlsiwaygmW7NPJpOZOF9GBjhGzdzZSsC2lEJ1IUkWLF4gMHlRA1aSA+Yw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz", + "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/notification": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-1.2.0.tgz", + "integrity": "sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/overflow": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.0.tgz", + "integrity": "sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/pagination": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.2.0.tgz", + "integrity": "sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/picker": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.9.0.tgz", + "integrity": "sha512-OLisdk8AWVCG9goBU1dWzuH5QlBQk8jktmQ6p0/IyBFwdKGwyIZOSjnBYo8hooHiTdl0lU+wGf/OfMtVBw02KQ==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/trigger": "^3.6.15", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@rc-component/portal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.0.tgz", + "integrity": "sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/progress": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.2.tgz", + "integrity": "sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.1.tgz", + "integrity": "sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/rate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz", + "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/resize-observer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.1.tgz", + "integrity": "sha512-NfXXMmiR+SmUuKE1NwJESzEUYUFWIDUn2uXpxCTOLwiRUUakd62DRNFjRJArgzyFW8S5rsL4aX5XlyIXyC/vRA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/segmented": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz", + "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/select": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.6.12.tgz", + "integrity": "sha512-jYXAglYdOb54BrpWAcjjhdBP16NyCv/HbEaWFMbEHZQAJVmGHPAtmBqbFuPPuvInAVsIwLbCj4Agag9udOamiQ==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.3.0", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/slider": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.0.1.tgz", + "integrity": "sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/steps": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.2.tgz", + "integrity": "sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz", + "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/table": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.9.1.tgz", + "integrity": "sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg==", + "license": "MIT", + "dependencies": { + "@rc-component/context": "^2.0.1", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.1.0", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tabs": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.7.0.tgz", + "integrity": "sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==", + "license": "MIT", + "dependencies": { + "@rc-component/dropdown": "~1.0.0", + "@rc-component/menu": "~1.2.0", + "@rc-component/motion": "^1.1.3", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/textarea": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/textarea/-/textarea-1.1.2.tgz", + "integrity": "sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.1.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tooltip": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.4.0.tgz", + "integrity": "sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.7.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.3.0.tgz", + "integrity": "sha512-K04K9r32kUC+auBSQfr+Fss4SpSIS9JGe56oq/ALAX0p+i2ylYOI1MgR83yBY7v96eO6ZFXcM/igCQmubps0Ow==", + "license": "MIT", + "dependencies": { + "@rc-component/portal": "^2.2.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.7.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tree": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.2.3.tgz", + "integrity": "sha512-mG8hF2ogQcKaEpfyxzPvMWqqkptofd7Sf+YiXOpPzuXLTLwNKfLDJtysc1/oybopbnzxNqWh2Vgwi+GYwNIb7w==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/util": "^1.8.1", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/tree-select": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.8.0.tgz", + "integrity": "sha512-iYsPq3nuLYvGqdvFAW+l+I9ASRIOVbMXyA8FGZg2lGym/GwkaWeJGzI4eJ7c9IOEhRj0oyfIN4S92Fl3J05mjQ==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.6.0", + "@rc-component/tree": "~1.2.0", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/trigger": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.9.0.tgz", + "integrity": "sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.2.0", + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/upload": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.0.tgz", + "integrity": "sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.9.0.tgz", + "integrity": "sha512-5uW6AfhIigCWeEQDthTozlxiT4Prn6xYQWeO0xokjcaa186OtwPRHBZJ2o0T0FhbjGhZ3vXdbkv0sx3gAYW7Vg==", + "license": "MIT", + "dependencies": { + "is-mobile": "^5.0.0", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.0.2.tgz", + "integrity": "sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.14.tgz", + "integrity": "sha512-OowOUbD1lBCOFIPOZ8xnMIhgqA4sCutMiYOmPHL1PTLt5+y1XA+g2+yC9OOyz8p+deMZqPZLxfMjYIfrKsPeFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.3.1.tgz", + "integrity": "sha512-8pRjvxitZFyrYAtgwml93Km7fCXjw9IeqlmzpIsusRsmO3eWFVrOMum6+0TsGCtR/WrXVnPwfsgrFg3ChzGCeA==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/cssinjs": "^2.1.0", + "@ant-design/cssinjs-utils": "^2.1.1", + "@ant-design/fast-color": "^3.0.1", + "@ant-design/icons": "^6.1.0", + "@ant-design/react-slick": "~2.0.0", + "@babel/runtime": "^7.28.4", + "@rc-component/cascader": "~1.14.0", + "@rc-component/checkbox": "~2.0.0", + "@rc-component/collapse": "~1.2.0", + "@rc-component/color-picker": "~3.1.0", + "@rc-component/dialog": "~1.8.4", + "@rc-component/drawer": "~1.4.2", + "@rc-component/dropdown": "~1.0.2", + "@rc-component/form": "~1.6.2", + "@rc-component/image": "~1.6.0", + "@rc-component/input": "~1.1.2", + "@rc-component/input-number": "~1.6.2", + "@rc-component/mentions": "~1.6.0", + "@rc-component/menu": "~1.2.0", + "@rc-component/motion": "^1.3.1", + "@rc-component/mutate-observer": "^2.0.1", + "@rc-component/notification": "~1.2.0", + "@rc-component/pagination": "~1.2.0", + "@rc-component/picker": "~1.9.0", + "@rc-component/progress": "~1.0.2", + "@rc-component/qrcode": "~1.1.1", + "@rc-component/rate": "~1.0.1", + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/segmented": "~1.3.0", + "@rc-component/select": "~1.6.12", + "@rc-component/slider": "~1.0.1", + "@rc-component/steps": "~1.2.2", + "@rc-component/switch": "~1.0.3", + "@rc-component/table": "~1.9.1", + "@rc-component/tabs": "~1.7.0", + "@rc-component/textarea": "~1.1.2", + "@rc-component/tooltip": "~1.4.0", + "@rc-component/tour": "~2.3.0", + "@rc-component/tree": "~1.2.3", + "@rc-component/tree-select": "~1.8.0", + "@rc-component/trigger": "^3.9.0", + "@rc-component/upload": "~1.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1", + "dayjs": "^1.11.11", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/echarts-for-react": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.6.tgz", + "integrity": "sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "size-sensor": "^1.0.1" + }, + "peerDependencies": { + "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "react": "^15.0.0 || >=16.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", + "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.3", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-mobile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz", + "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/size-sensor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.3.tgz", + "integrity": "sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==", + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..265585d --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "pms-react-new", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@types/file-saver": "^2.0.7", + "antd": "^6.3.1", + "axios": "^1.13.5", + "dayjs": "^1.11.19", + "echarts": "^6.0.0", + "echarts-for-react": "^3.0.6", + "file-saver": "^2.0.5", + "js-cookie": "^3.0.5", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..ee9fada --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..4a335ae --- /dev/null +++ b/src/App.css @@ -0,0 +1 @@ +/* Remove default styles */ diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..791ac7f --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,113 @@ +import { BrowserRouter as Router, Routes, Route, Navigate, Outlet, useLocation } from 'react-router-dom'; +import { Spin } from 'antd'; +import LoginPage from './pages/Login'; +import JobMonitorPage from './pages/monitor/JobMonitorPage'; +import CacheMonitorPage from './pages/monitor/CacheMonitorPage'; +import LoginLogPage from './pages/monitor/LoginLogPage'; // Import LoginLogPage +import OnlineUserPage from './pages/monitor/OnlineUserPage'; // Import OnlineUserPage +import OperationLogPage from './pages/monitor/OperationLogPage'; // Import OperationLogPage +import ServerMonitorPage from './pages/monitor/ServerMonitorPage'; // Import ServerMonitorPage +import CacheListPage from './pages/monitor/CacheListPage'; // Import CacheListPage +import UserPage from './pages/system/UserPage'; // Import UserPage +import RolePage from './pages/system/RolePage'; // Import RolePage +import MenuPage from './pages/system/MenuPage'; // Import MenuPage +import DeptPage from './pages/system/DeptPage'; // Import DeptPage +import DictPage from './pages/system/DictPage'; // Import DictPage +import ConfigPage from './pages/system/ConfigPage'; // Import ConfigPage +import ProjectPage from './pages/project/ProjectPage'; // Import ProjectPage +import ProjectDetailPage from './pages/project/ProjectDetailPage'; // Import ProjectDetailPage +import DemandManagePage from './pages/project/DemandManagePage'; // Import DemandManagePage +import TaskSetPage from './pages/workAppraisal/TaskSetPage'; // Import TaskSetPage +import ManagerPage from './pages/workAppraisal/ManagerPage'; +import ManagerUserPage from './pages/workAppraisal/ManagerUserPage'; +import AppraisalDetailPage from './pages/workAppraisal/AppraisalDetailPage'; // Import AppraisalDetailPage +import NormalWorkerPage from './pages/workAppraisal/NormalWorkerPage'; +import AppraisalDashboardPage from './pages/workAppraisal/AppraisalDashboardPage'; +import AppraisalModuleDetailPage from './pages/workAppraisal/AppraisalModuleDetailPage'; +import WorkLogPage from './pages/worklog/WorkLogPage'; // Import WorkLogPage +import ProjectExecutionPage from './pages/dashboard/ProjectExecutionPage'; +import ProjectUserPage from './pages/projectBank/ProjectUserPage'; +import UserProjectPage from './pages/projectBank/UserProjectPage'; +import UserScorePage from './pages/projectBank/UserScorePage'; +import UserScoreDetailPage from './pages/projectBank/UserScoreDetailPage'; +import ProfilePage from './pages/Profile'; +import MainLayout from './layout/MainLayout'; +import './App.css'; +import { getToken } from './utils/auth'; +import { PermissionProvider, usePermission } from './contexts/PermissionContext'; + +const PrivateRoute = () => { + const location = useLocation(); + const token = getToken(); + const { ready, loading, canAccessPath } = usePermission(); + + if (!token) { + return ; + } + + if (!ready || loading) { + return ( +
+ +
+ ); + } + + if (!canAccessPath(location.pathname)) { + return ; + } + + return ; +}; + + +function App() { + return ( + + + } /> + }> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + ); +} + +export default App; diff --git a/src/api/appraisal.ts b/src/api/appraisal.ts new file mode 100644 index 0000000..6de2457 --- /dev/null +++ b/src/api/appraisal.ts @@ -0,0 +1,127 @@ +import request from '@/utils/request'; + +// ========================================================== +// Task Appraisal related functions, from api.js -> taskApi +// ========================================================== + +// 获取考核任务列表 +export function getTaskList(data: any) { + return request({ + url: '/task/get', + method: 'get', + params: data, + }); +} + +// 获取当前用户考核任务列表(经理端) +export function getTaskListSelf(data?: any) { + return request({ + url: '/task/list', + method: 'get', + params: data, + }); +} + +// 获取普通员工考核任务列表(评分端) +export function getTaskListSelfNormal(data?: any) { + return request({ + url: '/task/listSelf', + method: 'get', + params: data, + }); +} + +// 新增考核任务 +export function addTask(data: any) { + return request({ + url: '/task/add', + method: 'post', + data: data, + }); +} + +// 修改考核任务 +export function updateTask(data: any) { + return request({ + url: '/task/update', + method: 'put', + data: data, + }); +} + +// 删除考核任务 +export function delTask(id: string | number) { + return request({ + url: `/task/${id}`, + method: 'delete', + }); +} + +// 获取任务的指标配置 +export function getTaskSet(id: string | number) { + return request({ + url: `/task/target/${id}`, + method: 'get', + }); +} + +// 保存任务的指标配置 +export function setTaskSet(data: any) { + return request({ + url: `/task/config/update`, + method: 'put', + data: data, + }); +} + +// 获取考核看板列表 +export function getTaskModel(params?: any) { + return request({ + url: '/examine/template/list', + method: 'get', + params, + }); +} + +// 获取考核看板指标详情 +export function getTaskModelSet(id: string | number) { + return request({ + url: `/examine/template/list/${id}`, + method: 'get', + }); +} + +// 删除考核看板 +export function delTaskModule(id: string | number) { + return request({ + url: `/examine/template/${id}`, + method: 'delete', + }); +} + +// 获取考核用户列表 (for manager view) +export function getTaskUserList(data: any) { + return request({ + url: '/examine/user', + method: 'get', + params: data, + }); +} + +// 获取考核分数详情 +export function getTaskScoreDetail(data: any) { + return request({ + url: '/examine/detail', + method: 'get', + params: data, + }); +} + +// 保存考核分数 +export function saveTaskUserScore(data: any) { + return request({ + url: '/examine/detail/batch', + method: 'post', + data: data, + }); +} diff --git a/src/api/login.ts b/src/api/login.ts new file mode 100644 index 0000000..28a1b63 --- /dev/null +++ b/src/api/login.ts @@ -0,0 +1,18 @@ +import request from '@/utils/request'; +import type { CaptchaResponse, LoginRequest, LoginResponse } from '@/types/api'; + +export function login(data: LoginRequest) { + return request({ + url: '/login', + method: 'post', + data, + }); +} + +export function getCodeImg() { + return request({ + url: '/captchaImage', + method: 'get', + headers: { isToken: false }, + }); +} diff --git a/src/api/monitor/cache.ts b/src/api/monitor/cache.ts new file mode 100644 index 0000000..e7064c4 --- /dev/null +++ b/src/api/monitor/cache.ts @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import type { + CacheKeyPayload, + CacheNamePayload, + CacheMonitorResponse, + CacheValueResponse, +} from '@/types/api'; + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get', + }); +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get', + }); +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName: string) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get', + }); +} + +// 查询缓存内容 +export function getCacheValue(cacheName: string, cacheKey: string) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get', + }); +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName: string) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }); +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheKey: string) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }); +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }); +} diff --git a/src/api/monitor/job.ts b/src/api/monitor/job.ts new file mode 100644 index 0000000..04395e3 --- /dev/null +++ b/src/api/monitor/job.ts @@ -0,0 +1,64 @@ +import request from '@/utils/request'; +import type { JobListResponse, JobQueryParams, JobRecord } from '@/types/api'; + +export function listJob(query: JobQueryParams) { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query, + }); +} + +export function getJob(jobId: JobRecord['jobId']) { + return request({ + url: `/monitor/job/${jobId}`, + method: 'get', + }); +} + +export function addJob(data: JobRecord) { + return request({ + url: '/monitor/job', + method: 'post', + data, + }); +} + +export function updateJob(data: JobRecord) { + return request({ + url: '/monitor/job', + method: 'put', + data, + }); +} + +export function delJob(jobId: string | number) { + return request({ + url: `/monitor/job/${jobId}`, + method: 'delete', + }); +} + +export function changeJobStatus(jobId: JobRecord['jobId'], status: string) { + const data = { + jobId, + status, + }; + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data, + }); +} + +export function runJob(jobId: JobRecord['jobId'], jobGroup: string) { + const data = { + jobId, + jobGroup, + }; + return request({ + url: '/monitor/job/run', + method: 'put', + data, + }); +} diff --git a/src/api/monitor/logininfor.ts b/src/api/monitor/logininfor.ts new file mode 100644 index 0000000..222392b --- /dev/null +++ b/src/api/monitor/logininfor.ts @@ -0,0 +1,34 @@ +import request from '@/utils/request'; +import type { + LogininforListResponse, + LogininforQueryParams, +} from '@/types/api'; + +export function listLogininfor(query: LogininforQueryParams) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query, + }); +} + +export function delLogininfor(infoId: string | number) { + return request({ + url: `/monitor/logininfor/${infoId}`, + method: 'delete', + }); +} + +export function unlockLogininfor(userName: string) { + return request({ + url: `/monitor/logininfor/unlock/${userName}`, + method: 'get', + }); +} + +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete', + }); +} diff --git a/src/api/monitor/online.ts b/src/api/monitor/online.ts new file mode 100644 index 0000000..339b5c8 --- /dev/null +++ b/src/api/monitor/online.ts @@ -0,0 +1,17 @@ +import request from '@/utils/request'; +import type { OnlineListResponse, OnlineQueryParams } from '@/types/api'; + +export function listOnline(query: OnlineQueryParams) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query, + }); +} + +export function forceLogout(tokenId: string) { + return request({ + url: `/monitor/online/${tokenId}`, + method: 'delete', + }); +} diff --git a/src/api/monitor/operlog.ts b/src/api/monitor/operlog.ts new file mode 100644 index 0000000..aacec14 --- /dev/null +++ b/src/api/monitor/operlog.ts @@ -0,0 +1,24 @@ +import request from '@/utils/request'; +import type { OperlogListResponse, OperlogQueryParams } from '@/types/api'; + +export function listOperlog(query: OperlogQueryParams) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query, + }); +} + +export function delOperlog(operId: string | number) { + return request({ + url: `/monitor/operlog/${operId}`, + method: 'delete', + }); +} + +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete', + }); +} diff --git a/src/api/monitor/server.ts b/src/api/monitor/server.ts new file mode 100644 index 0000000..2b37f5e --- /dev/null +++ b/src/api/monitor/server.ts @@ -0,0 +1,9 @@ +import request from '@/utils/request'; +import type { ServerInfoResponse } from '@/types/api'; + +export function getServerInfo() { + return request({ + url: '/monitor/server', + method: 'get', + }); +} diff --git a/src/api/permission.ts b/src/api/permission.ts new file mode 100644 index 0000000..c349817 --- /dev/null +++ b/src/api/permission.ts @@ -0,0 +1,37 @@ +import request from '@/utils/request'; + +export interface RouterNode { + path?: string; + hidden?: boolean; + alwaysShow?: boolean; + component?: string; + name?: string; + meta?: { + title?: string; + icon?: string; + [key: string]: unknown; + }; + children?: RouterNode[]; + [key: string]: unknown; +} + +export interface UserInfoResponse { + user?: Record; + roles?: Array>; + permissions?: string[]; + [key: string]: unknown; +} + +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get', + }); +} + +export function getRouters() { + return request({ + url: '/getRouters', + method: 'get', + }); +} diff --git a/src/api/project.ts b/src/api/project.ts new file mode 100644 index 0000000..04b3661 --- /dev/null +++ b/src/api/project.ts @@ -0,0 +1,180 @@ +import request from '@/utils/request'; + +// ========================================================== +// Project related functions, from api.js +// ========================================================== + +// 查询项目列表 +export function listProject(query: any) { + return request({ + url: '/business/project/list', + method: 'get', + params: query, + }); +} + +// 删除项目 +export function deleteProject(id: string | number) { + return request({ + url: `/business/project/${id}`, + method: 'delete', + }); +} + +// 新增项目 +export function addProject(data: any) { + return request({ + url: '/business/project/add', + method: 'post', + data: data, + }); +} + +// 修改项目 +export function updateProject(data: any) { + return request({ + url: '/business/project/update', + method: 'put', + data: data, + }); +} + +// 获取项目编号 +export function getProjectCode() { + return request({ + url: '/business/project/getCode', + method: 'get', + }); +} + +// 获取项目详情 +export function getProjectDetail(id: string | number) { + return request({ + url: `/business/project/info/${id}`, + method: 'get', + }); +} + +// 获取项目成员 +export function getProjectUser(id: string | number) { + return request({ + url: `/business/project/${id}`, + method: 'get', + }); +} + +// 更新项目成员 +export function updateProjectUser(data: any) { + return request({ + url: '/business/project/team', + method: 'POST', + data: data, + }); +} + +// 删除项目成员 +export function deleteProjectUser(id: string | number) { + return request({ + url: `/business/project/team/${id}`, + method: 'delete', + }); +} + +// 检查项目是否有日志数据 +export function projectHasLogData(data: any) { + return request({ + url: `/business/project/updateCheck`, + method: 'POST', + data: data, + }); +} + +// ========================================================== +// Demand & Version APIs (provided by user) +// ========================================================== + +// 需求列表 +export function listDemand(query: any) { + return request({ + url: '/demand/list', + method: 'get', + params: query, + }); +} + +// 需求编辑 +export function updateDemand(data: any) { + return request({ + url: '/demand/update', + method: 'put', + data, + }); +} + +// 新增需求 +export function insertDemand(data: any) { + return request({ + url: '/demand/insert', + method: 'post', + data, + }); +} + +// 删除需求 +export function deleteDemand(id: string | number) { + return request({ + url: `/demand/${id}`, + method: 'delete', + }); +} + +// 批量删除需求 +export function deleteDemandBatch(ids: string) { + return request({ + url: `/demand/remove/batch/${ids}`, + method: 'delete', + }); +} + +// 需求详情 +export function getDemandDetail(id: string | number) { + return request({ + url: `/demand/${id}`, + method: 'get', + }); +} + +// 新增版本 +export function insertProjectVersion(data: any) { + return request({ + url: '/projectVersion/insert', + method: 'post', + data, + }); +} + +// 编辑版本 +export function updateProjectVersion(data: any) { + return request({ + url: '/projectVersion/update', + method: 'put', + data, + }); +} + +// 删除版本 +export function deleteProjectVersion(id: string | number) { + return request({ + url: `/projectVersion/${id}`, + method: 'delete', + }); +} + +// 版本树/版本列表 +export function listProjectVersionTree(projectId: string | number) { + return request({ + url: '/projectVersion/tree', + method: 'get', + params: { projectId }, + }); +} diff --git a/src/api/projectExecution.ts b/src/api/projectExecution.ts new file mode 100644 index 0000000..4598974 --- /dev/null +++ b/src/api/projectExecution.ts @@ -0,0 +1,27 @@ +import request from '@/utils/request'; + +export function listProjectExecution(query: Record) { + return request({ + url: '/projectBank/projectProgress/list', + method: 'get', + params: query, + }); +} + +export function getProjectExecutionInfo(data: Record) { + return request({ + url: '/business/project/executionInfo', + method: 'post', + data, + timeout: 20000, + }); +} + +export function getProjectWorkInfo(data: Record) { + return request({ + url: '/business/project/workInfo', + method: 'post', + data, + timeout: 20000, + }); +} diff --git a/src/api/projectScore.ts b/src/api/projectScore.ts new file mode 100644 index 0000000..bd5f314 --- /dev/null +++ b/src/api/projectScore.ts @@ -0,0 +1,45 @@ +import request from '@/utils/request'; + +const LIST_FALLBACKS = ['/business/project/scoreInfo', '/business/project/userScore']; +const DETAIL_FALLBACKS = ['/business/project/scoreDetail', '/business/project/userScoreDetail']; + +const callWithFallback = async ( + urls: string[], + payload: Record, +): Promise => { + let lastError: unknown; + + for (const url of urls) { + try { + return await request({ + url, + method: 'post', + data: payload, + timeout: 20000, + }); + } catch (error) { + lastError = error; + } + + try { + return await request({ + url, + method: 'get', + params: payload, + timeout: 20000, + }); + } catch (error) { + lastError = error; + } + } + + throw lastError ?? new Error('接口请求失败'); +}; + +export function getProjectUserScoreList(payload: Record) { + return callWithFallback(LIST_FALLBACKS, payload); +} + +export function getProjectUserScoreDetail(payload: Record) { + return callWithFallback(DETAIL_FALLBACKS, payload); +} diff --git a/src/api/system/config.ts b/src/api/system/config.ts new file mode 100644 index 0000000..890ec34 --- /dev/null +++ b/src/api/system/config.ts @@ -0,0 +1,60 @@ +import request from '@/utils/request'; + +// 查询参数列表 +export function listConfig(query: any) { + return request({ + url: '/system/config/list', + method: 'get', + params: query, + }); +} + +// 查询参数详细 +export function getConfig(configId: string | number) { + return request({ + url: '/system/config/' + configId, + method: 'get', + }); +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey: string) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get', + }); +} + +// 新增参数配置 +export function addConfig(data: any) { + return request({ + url: '/system/config', + method: 'post', + data: data, + }); +} + +// 修改参数配置 +export function updateConfig(data: any) { + return request({ + url: '/system/config', + method: 'put', + data: data, + }); +} + +// 删除参数配置 +export function delConfig(configId: string | number) { + return request({ + url: '/system/config/' + configId, + method: 'delete', + }); +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete', + }); +} diff --git a/src/api/system/dept.ts b/src/api/system/dept.ts new file mode 100644 index 0000000..d5cc8b6 --- /dev/null +++ b/src/api/system/dept.ts @@ -0,0 +1,52 @@ +import request from '@/utils/request'; + +// 查询部门列表 +export function listDept(query?: any) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query, + }); +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId: string | number) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get', + }); +} + +// 查询部门详细 +export function getDept(deptId: string | number) { + return request({ + url: '/system/dept/' + deptId, + method: 'get', + }); +} + +// 新增部门 +export function addDept(data: any) { + return request({ + url: '/system/dept', + method: 'post', + data: data, + }); +} + +// 修改部门 +export function updateDept(data: any) { + return request({ + url: '/system/dept', + method: 'put', + data: data, + }); +} + +// 删除部门 +export function delDept(deptId: string | number) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete', + }); +} diff --git a/src/api/system/dict.ts b/src/api/system/dict.ts new file mode 100644 index 0000000..b4ee086 --- /dev/null +++ b/src/api/system/dict.ts @@ -0,0 +1,115 @@ +import request from '@/utils/request'; + +// 查询字典类型列表 +export function listDictType(query: any) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query, + }); +} + +// 查询字典类型详细 +export function getDictType(dictId: string | number) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get', + }); +} + +// 新增字典类型 +export function addDictType(data: any) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data, + }); +} + +// 修改字典类型 +export function updateDictType(data: any) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data, + }); +} + +// 删除字典类型 +export function delDictType(dictId: string | number) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete', + }); +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete', + }); +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get', + }); +} + +// ========================================================== +// DictData related functions, from api/system/dict/data.js +// ========================================================== + +// 查询字典数据列表 +export function listDictData(query: any) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query, + }); +} + +// 查询字典数据详细 +export function getDictData(dictCode: string | number) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get', + }); +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType: string) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get', + }); +} + +// 新增字典数据 +export function addDictData(data: any) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data, + }); +} + +// 修改字典数据 +export function updateDictData(data: any) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data, + }); +} + +// 删除字典数据 +export function delDictData(dictCode: string | number) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete', + }); +} diff --git a/src/api/system/menu.ts b/src/api/system/menu.ts new file mode 100644 index 0000000..74da161 --- /dev/null +++ b/src/api/system/menu.ts @@ -0,0 +1,60 @@ +import request from '@/utils/request'; + +// 查询菜单列表 +export function listMenu(query: any) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query, + }); +} + +// 查询菜单详细 +export function getMenu(menuId: string | number) { + return request({ + url: '/system/menu/' + menuId, + method: 'get', + }); +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get', + }); +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId: string | number) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get', + }); +} + +// 新增菜单 +export function addMenu(data: any) { + return request({ + url: '/system/menu', + method: 'post', + data: data, + }); +} + +// 修改菜单 +export function updateMenu(data: any) { + return request({ + url: '/system/menu', + method: 'put', + data: data, + }); +} + +// 删除菜单 +export function delMenu(menuId: string | number) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete', + }); +} diff --git a/src/api/system/role.ts b/src/api/system/role.ts new file mode 100644 index 0000000..c5922a5 --- /dev/null +++ b/src/api/system/role.ts @@ -0,0 +1,119 @@ +import request from '@/utils/request'; + +// 查询角色列表 +export function listRole(query: any) { + return request({ + url: '/system/role/list', + method: 'get', + params: query, + }); +} + +// 查询角色详细 +export function getRole(roleId: string | number) { + return request({ + url: '/system/role/' + roleId, + method: 'get', + }); +} + +// 新增角色 +export function addRole(data: any) { + return request({ + url: '/system/role', + method: 'post', + data: data, + }); +} + +// 修改角色 +export function updateRole(data: any) { + return request({ + url: '/system/role', + method: 'put', + data: data, + }); +} + +// 角色数据权限 +export function dataScope(data: any) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data, + }); +} + +// 角色状态修改 +export function changeRoleStatus(roleId: string | number, status: string) { + const data = { + roleId, + status, + }; + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data, + }); +} + +// 删除角色 +export function delRole(roleId: string | number) { + return request({ + url: '/system/role/' + roleId, + method: 'delete', + }); +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query: any) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query, + }); +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query: any) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query, + }); +} + +// 取消用户授权角色 +export function authUserCancel(data: any) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data, + }); +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data: any) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data, + }); +} + +// 授权用户选择 +export function authUserSelectAll(data: any) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data, + }); +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId: string | number) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get', + }); +} diff --git a/src/api/system/user.ts b/src/api/system/user.ts new file mode 100644 index 0000000..01fea5f --- /dev/null +++ b/src/api/system/user.ts @@ -0,0 +1,136 @@ +import request from '@/utils/request'; +import { parseStrEmpty } from '@/utils/ruoyi'; + +// 查询用户列表 +export function listUser(query: any) { + return request({ + url: '/system/user/list', + method: 'get', + params: query, + }); +} + +// 查询用户详细 +export function getUser(userId: string | number) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get', + }); +} + +// 新增用户 +export function addUser(data: any) { + return request({ + url: '/system/user', + method: 'post', + data: data, + }); +} + +// 修改用户 +export function updateUser(data: any) { + return request({ + url: '/system/user', + method: 'put', + data: data, + }); +} + +// 删除用户 +export function delUser(userId: string | number) { + return request({ + url: '/system/user/' + userId, + method: 'delete', + }); +} + +// 用户密码重置 +export function resetUserPwd(userId: string | number, password: string) { + const data = { + userId, + password, + }; + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data, + }); +} + +// 用户状态修改 +export function changeUserStatus(userId: string | number, status: string) { + const data = { + userId, + status, + }; + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data, + }); +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get', + }); +} + +// 修改用户个人信息 +export function updateUserProfile(data: any) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data, + }); +} + +// 用户密码重置 (Profile page specific) +export function updateUserPwd(oldPassword: string, newPassword: string) { + const data = { + oldPassword, + newPassword, + }; + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data, + }); +} + +// 用户头像上传 +export function uploadAvatar(data: any) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: data, + }); +} + +// 查询授权角色 +export function getAuthRole(userId: string | number) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get', + }); +} + +// 保存授权角色 +export function updateAuthRole(data: any) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data, + }); +} + +// 查询部门下拉树结构 +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get', + }); +} diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..6c6b23a --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,32 @@ +import request from '@/utils/request'; +import type { + UpdateUserProfilePayload, + UserProfileResponse, +} from '@/types/api'; + +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get', + }); +} + +export function updateUserProfile(data: UpdateUserProfilePayload) { + return request({ + url: '/system/user/profile', + method: 'put', + data, + }); +} + +export function updateUserPwd(oldPassword: string, newPassword: string) { + const data = { + oldPassword, + newPassword, + }; + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data, + }); +} diff --git a/src/api/worklog.ts b/src/api/worklog.ts new file mode 100644 index 0000000..afd00fe --- /dev/null +++ b/src/api/worklog.ts @@ -0,0 +1,91 @@ +import request from '@/utils/request'; + +// ========================================================== +// Work Log related functions, from api.js -> workLogApi +// ========================================================== + +// 获取用户项目列表 +export function userProject(userId: string | number) { + return request({ + url: `/business/work/hour/project/${userId}`, + method: 'get', + }); +} + +// 获取日志数据 +export function getLogData(query: any) { + return request({ + url: '/business/work/hour/calendar', + method: 'post', + data: query, + }); +} + +// 获取日志详情 +export function getLogDataDetail(query: any) { + return request({ + url: '/business/work/hour/getInfo', + method: 'post', + data: query, + }); +} + +// 获取当天可用工时 +export function getDayTime(query: any) { + return request({ + url: '/business/work/hour/remaining', + method: 'post', + data: query, + }); +} + +// 新增日志 +export function addLog(data: any) { + return request({ + url: '/business/work/hour/add', + method: 'post', + data: data, + }); +} + +// 编辑日志 +export function editLog(data: any) { + return request({ + url: '/business/work/hour/update', + method: 'put', + data: data, + }); +} + +// 删除日志 +export function delLog(id: string | number) { + return request({ + url: `/business/work/hour/${id}`, + method: 'delete', + }); +} + +// 工作日志版本树(用于日志录入时选择版本/需求) +export function getVersionTreeForWorklog(params: any) { + return request({ + url: '/projectVersion/tree', + method: 'get', + params, + }); +} + +// 删除项目附件 +export function deleteProjectFile(id: string | number) { + return request({ + url: `/business/project/file/${id}`, + method: 'delete', + }); +} + +// 批量删除项目附件 +export function deleteProjectFileBatch(ids: string) { + return request({ + url: `/business/project/file/batch/${ids}`, + method: 'delete', + }); +} diff --git a/src/assets/error/404.png b/src/assets/error/404.png new file mode 100644 index 0000000..fb8199f --- /dev/null +++ b/src/assets/error/404.png @@ -0,0 +1 @@ +iVBORw0KGgoAAAANSUhEUgAAAioAAAHbCAYAAACw33E7AAAgAElEQVR4nOy9d5xcV3nvf7t+Vfd+rU8vvdIDCRAQCBAEBERExV0URVEREVFEFAFFxQXsuBfEioiCIgiCe+/tPb3eU++p6j33/f6xZz2z1j27u2d2d9/l+Xk+eM0zZ87MvrP2Xmvf2hEREBERAREQAREQAREQAREQgRkIjBkhgIiIiIiIiIiIiIiIiAgyhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQAREQgRCFCkBERAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkBERAREQAREQAREQEQghUKIiIiIiIiIiIiIiIiIgAhChEQEREBERAREQAREQAREQEIQQqREQEREBERAREQAREQAREQgRCFCkberEIAAAACBjQAEj/YC/9k= diff --git a/src/assets/login-background.jpg b/src/assets/login-background.jpg new file mode 100644 index 0000000..9e31d16 --- /dev/null +++ b/src/assets/login-background.jpg @@ -0,0 +1 @@ +g/9j/4AAQSkZJRgABAQEASABIAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAAAAAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEAAQBIAAAAAQABOEJJTQQGAAAAAAAEAAAAAjhCSU0EAgAAAAAABAAAAAD/4Q/gaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0MCA3OS4xNjA0NTEsIDIwMTcvMDUvMDYtMDE6MDg6MjEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6cGRmPSJodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIiBwZGY6UHJvZHVjZXI9IkNvbnZlcnQgZnJvbSBBcHBsZSBCb29rIHRvIFBERiAoQXBwbGUgSW5jLikgLSBRdWFydHogMi4wIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkZBNjczQjlCQjNGNDExRTk4NjY0QzZERjhDMzYxNTMyIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkZBNjczQjlBQjNGNDExRTk4NjY0QzZERjhDMzYxNTMyIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzIyQjE1NTM4RjM4MTFFOUExM0Q4RjhBRjE3RTBDRDQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzIyQjE1NTQ4RjM4MTFFOUExM0Q4RjhBRjE3RTBDRDQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAOGAAEABgAGgAfABdhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGZ3dHB0AAABwAAAABRia3B0AAAB1AAAABRyWFlaAAAB7AAAABRnWFlaAAACAAAAABRiWFlaAAACFAAAABRyVFJDAAACGAAQAMBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRsdW1pAAACjAAAAA埬 diff --git a/src/assets/pay.png b/src/assets/pay.png new file mode 100644 index 0000000..b20caec --- /dev/null +++ b/src/assets/pay.png @@ -0,0 +1 @@ +iVBORw0KGgoAAAANSUhEUgAAAoAAAAFoCAYAAABq2s2dAAAKpmlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N71QkhCKlNBraFICSA29SJEuKjQhSYkYFEVFFRWLgqLgBFExgSJoQBBQYMmCIoKiouIFAVFFQREREREFFeHn/9/f7O/953vO+V7u+d/33n3nN4CAE/oA4CExzYskABiYmJjZ2Tn5+T8/I8AUEhYSDgoPCQ8REhEVFRkXFxkfHR8lJyYpLS4vNTZATE5MU11eX19fYGFiY2RlZmdoaWprbW9pam1vb3BxcXN0dXd4eXp7fH1+f4CBg4SFh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dYXA2FvUP+9p4+fn5/f4+v3/As6/7E/R2fj4+L8/Lv7Rfr5/L/8D/zP/iAAdM0QBhAIAhDAEEgYlgAIAiDAZgAsLChEMMAh2AhYBEYCLoCFgEmgGlgFngGfga+BqYCFYEKwRjBGcEywQvBDsEOARHBEwEUwRjBGcEJwQrBC8EOwQ9BFkEXQRzBHMEVwR3BIIElQSuBLcEuQTDhGEEnYSNBJlEm4SBhJvEkASVBKGErASvhLGEt4S7hMMEwMTBhMGEyYTRhNOE4YTrhPWE/4TNhO6E84T5hP+FCYUXhROCx4UThT+VB4WLhY2FG4UrhVuFq4W2hfeFz4VnhdeF94Z3hzeGj4dPhneHj4ZfhSeGn4avh2+Gr4fXh/eHz4d/h9+Kj4nfiR+L/4k/it+Jn4tfih+LL4s/i9+L34ufiB+Mv4r/jb+PP49/jD+P/5B/l3+R/6A/p3+V/4w/r3+T/4f/sAAVAhmBGsEIAQfBEAEJwQPBFIEVgR8BGQEYQRtBOgEfwR/BGUEcAR+BJUEiQSzBKMEqgS+BNMEwwTTBNME4wTDBOWE8wT1hPiE9QT9BQ8FEwUzBTcFSQLdBQ0FTwUrBYIFhQWqBYUFgQXFBYYFvQXABcQF0wXTBe8FyAXaBdoF+wX7Bg4GKwZLBmMGRwYtBm8GdgaZBq0GswaTBqcGvgbWBtIG3wbVBv8HAwcxBzsHLwc9B1EHSgdlB28HcAdcB34Hggd/B4cHoQeLh5AHoQedB64HrwfNB8EH0wfjB94H1wffB+8H9Af0B/YH/gf9B/4H/wf/BwEHgQdBBkEGQQ/BEAEZwYyBL0FwwTDBPME4wTjBN0E7QT/BQ0FKwWDBYUFqgWFBZcFyAXTBdMF5wX+BgcGIwYzBkMGRwa1BnsGsgbSBtMG5wbnBwsHIwc7By8HQAdIB2UHbweAB24HfQeGB4kHkgeVB5sHrwfBB8cH2wfvB/QH9Qf3B/4H/wf/B/8HAQeDBEEGQwYhBjkGQwY9Bl0GcwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGQwZDBkMGCBAgECBAoGFAIBBBIECAQIBggQDBAkGDAYKDA0OFBcaGCg2PDRAQ0pHVFVdX2BjaG1wc3R3ent/g4eLj5OXm5+jp6uvs7fDx8vP19/v8AAEBBAQGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHx8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdocWpua29zdHV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v8AAQIEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaHFqc3V5f4KMl56wtr6/xMjP0tff4uXq7/P3/v8AAQIECg4UGBwgLDBAUFhgZGiAiJCYoKiwuMDQ4PEBESExQVFhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaHFqb3F0eXp9f4OFj5SanqKvsr/Gy9HT2t7h5u3w9fn+//8BAgMICAwOEBQWGhweICEiJygtNT5ESktPUVZXWFpcXV9fY2RmaWpqbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v//wAABAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdocWpua29zdHV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v8AAQEEAgUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hxanN1eX+CjJeer7GzwM/T2N3i5uvw9fj+//8CAwQEBQUGBwgICQoLDAwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaHFqc3V5f4KMl56wsbO9wsnO0dXa3eHn7fD2+f7/AgQFBggIDg8TFhscHyEmKCkwNjg8QUpOUldeYGVqdHx9goeLj5OYm6Glq7W3u8bJz9XY2+Dp6/L09/v9AAICAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdocWpua29zdHV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v//AQIBAQICAwMEBAUFBgYHBw== diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..8e0e0f1 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx new file mode 100644 index 0000000..b2c7f1c --- /dev/null +++ b/src/components/Navbar/index.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Button, Space } from 'antd'; +import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import { removeToken } from '../../utils/auth'; +import { usePermission } from '@/contexts/PermissionContext'; + +interface AppNavbarProps { + collapsed: boolean; + onToggle: () => void; +} +const AppNavbar: React.FC = ({ collapsed, onToggle }) => { + const navigate = useNavigate(); + const { userName } = usePermission(); + + const handleLogout = () => { + removeToken(); + navigate('/login'); + }; + + return ( +
+ + + +
+ ); +}; + +export default AppNavbar; diff --git a/src/components/PageBackButton.tsx b/src/components/PageBackButton.tsx new file mode 100644 index 0000000..c053f90 --- /dev/null +++ b/src/components/PageBackButton.tsx @@ -0,0 +1,29 @@ +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import { useNavigate } from 'react-router-dom'; + +interface PageBackButtonProps { + text?: string; + fallbackPath?: string; + className?: string; +} + +const PageBackButton = ({ text = '返回', fallbackPath = '/', className }: PageBackButtonProps) => { + const navigate = useNavigate(); + + const handleBack = () => { + if (window.history.length > 1) { + navigate(-1); + return; + } + navigate(fallbackPath); + }; + + return ( + + ); +}; + +export default PageBackButton; diff --git a/src/components/Permission/index.tsx b/src/components/Permission/index.tsx new file mode 100644 index 0000000..dad88b4 --- /dev/null +++ b/src/components/Permission/index.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { usePermission } from '@/contexts/PermissionContext'; + +interface PermissionProps { + permissions?: string | string[]; + roles?: string | string[]; + mode?: 'and' | 'or'; + children: React.ReactNode; + fallback?: React.ReactNode; +} + +const Permission: React.FC = ({ + permissions, + roles, + mode = 'or', + children, + fallback = null, +}) => { + const { hasPermi, hasRole } = usePermission(); + + const permiPassed = permissions ? hasPermi(permissions) : true; + const rolePassed = roles ? hasRole(roles) : true; + + let passed = true; + if (permissions && roles) { + passed = mode === 'and' ? permiPassed && rolePassed : permiPassed || rolePassed; + } else if (permissions) { + passed = permiPassed; + } else if (roles) { + passed = rolePassed; + } + + if (!passed) { + return <>{fallback}; + } + return <>{children}; +}; + +export default Permission; diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx new file mode 100644 index 0000000..7a17abb --- /dev/null +++ b/src/components/Sidebar/index.tsx @@ -0,0 +1,283 @@ +import React from 'react'; +import { Menu } from 'antd'; +import type { MenuProps } from 'antd'; +import { + AppstoreOutlined, + AreaChartOutlined, + AuditOutlined, + BarChartOutlined, + BookOutlined, + CarryOutOutlined, + ClockCircleOutlined, + CodeSandboxOutlined, + ControlOutlined, + DashboardOutlined, + DatabaseOutlined, + DesktopOutlined, + EditOutlined, + FileTextOutlined, + LockOutlined, + MonitorOutlined, + PieChartOutlined, + ProjectOutlined, + SettingOutlined, + TableOutlined, + TeamOutlined, + ToolOutlined, + UnorderedListOutlined, + UserOutlined, +} from '@ant-design/icons'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { usePermission } from '@/contexts/PermissionContext'; +import type { RouterNode } from '@/api/permission'; + +type MenuItem = Required['items'][number]; + +const BACKEND_PATH_TO_APP_PATH: Record = { + '/project': '/project/list', + '/projectBank/userScore': '/workAppraisal/myPerformance', +}; + +const MENU_ROUTE_ALIASES: Record = { + '/projectBank/projectProgress': ['/dashboard/project-execution'], + '/workAppraisal/myPerformance': ['/projectBank/userScore'], +}; + +const ICON_MAP: Record = { + edit: , + chart: , + table: , + peoples: , + dashboard: , + tab: , + excel: , + documentation: , + build: , + log: , + druid: , + system: , + user: , + tree: , + 'tree-table': , + dict: , + monitor: , + time: , + job: , + online: , + server: , + cache: , + form: , + documentation2: , +}; + +const FALLBACK_ICON_BY_PATH: Array<{ prefix: string; icon: React.ReactNode }> = [ + { prefix: '/index', icon: }, + { prefix: '/monitor/online', icon: }, + { prefix: '/monitor/job', icon: }, + { prefix: '/monitor/cache', icon: }, + { prefix: '/monitor/server', icon: }, + { prefix: '/monitor/cacheList', icon: }, + { prefix: '/monitor/logininfor', icon: }, + { prefix: '/monitor/operlog', icon: }, + { prefix: '/system/user', icon: }, + { prefix: '/system/role', icon: }, + { prefix: '/system/menu', icon: }, + { prefix: '/system/dept', icon: }, + { prefix: '/system/dict', icon: }, + { prefix: '/system/config', icon: }, + { prefix: '/project/list', icon: }, + { prefix: '/projectBank/projectProgress', icon: }, + { prefix: '/projectBank/projectUser', icon: }, + { prefix: '/projectBank/userProject', icon: }, + { prefix: '/workAppraisal/myPerformance', icon: }, + { prefix: '/workAppraisal/manager', icon: }, + { prefix: '/workAppraisal/normalWorker', icon: }, + { prefix: '/workAppraisal/taskSet', icon: }, + { prefix: '/workAppraisal/taskModule', icon: }, +]; + +const normalizePath = (value: string) => { + if (!value) { + return '/'; + } + const path = value.startsWith('/') ? value : `/${value}`; + return path.replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; +}; + +const joinPath = (base: string, child: string) => { + if (!child) { + return normalizePath(base); + } + if (child.startsWith('/')) { + return normalizePath(child); + } + if (base === '/' || !base) { + return normalizePath(`/${child}`); + } + return normalizePath(`${base}/${child}`); +}; + +const resolveAppPath = (fullBackendPath: string, node: RouterNode) => { + const normalized = normalizePath(fullBackendPath); + if (normalized === '/index' || String(node.component ?? '').includes('worklog')) { + return '/index'; + } + if (normalized === '/project' || String(node.component ?? '') === 'project/list') { + return '/project/list'; + } + return BACKEND_PATH_TO_APP_PATH[normalized] ?? normalized; +}; + +const getMenuIcon = (icon?: string, resolvedPath?: string) => { + const iconKey = String(icon ?? '').trim(); + if (iconKey && ICON_MAP[iconKey]) { + return ICON_MAP[iconKey]; + } + const fallback = FALLBACK_ICON_BY_PATH.find((item) => resolvedPath === item.prefix || String(resolvedPath).startsWith(`${item.prefix}/`)); + return fallback?.icon ?? ; +}; + +const isHiddenRoute = (node: RouterNode) => Boolean(node.hidden); + +const getRouteTitle = (node: RouterNode) => String(node.meta?.title ?? node.name ?? '').trim(); + +const buildMenuItems = ( + nodes: RouterNode[], + parentPath: string, + canAccessPath: (path: string) => boolean, +): MenuItem[] => { + return nodes.flatMap((node, index) => { + if (isHiddenRoute(node)) { + return []; + } + + const rawPath = joinPath(parentPath, String(node.path ?? '')); + const resolvedPath = resolveAppPath(rawPath, node); + const title = getRouteTitle(node); + const children = Array.isArray(node.children) ? buildMenuItems(node.children, rawPath, canAccessPath) : []; + + if (children.length > 0) { + if (!node.alwaysShow && children.length === 1 && title) { + return children; + } + if (!title) { + return children; + } + const groupKeySeed = String(node.path ?? node.name ?? resolvedPath ?? index); + return [{ + key: `group_${groupKeySeed}_${index}`, + icon: getMenuIcon(String(node.meta?.icon ?? ''), resolvedPath), + label: title, + children, + }]; + } + + if (!title || !resolvedPath || !canAccessPath(resolvedPath)) { + return []; + } + + return [{ + key: resolvedPath, + icon: getMenuIcon(String(node.meta?.icon ?? ''), resolvedPath), + label: title, + }]; + }); +}; + +const isRouteKey = (key: React.Key): key is string => typeof key === 'string' && key.startsWith('/'); + +const findMatchedPath = (menuItems: MenuItem[], pathname: string): string | null => { + for (const item of menuItems) { + if (!item) { + continue; + } + + const children = item.children as MenuItem[] | undefined; + if (Array.isArray(children) && children.length > 0) { + const childHit = findMatchedPath(children, pathname); + if (childHit) { + return childHit; + } + } + + if (isRouteKey(item.key)) { + const aliases = MENU_ROUTE_ALIASES[item.key] ?? []; + if ( + item.key === pathname || + pathname.startsWith(`${item.key}/`) || + aliases.some((alias) => alias === pathname || pathname.startsWith(`${alias}/`)) + ) { + return item.key; + } + } + } + return null; +}; + +const findParentKeys = ( + menuItems: MenuItem[], + targetKey: string, + parentKeys: string[] = [], +): string[] | null => { + for (const item of menuItems) { + if (!item) { + continue; + } + + const key = String(item.key); + if (key === targetKey) { + return parentKeys; + } + + const children = item.children as MenuItem[] | undefined; + if (Array.isArray(children) && children.length > 0) { + const found = findParentKeys(children, targetKey, [...parentKeys, key]); + if (found) { + return found; + } + } + } + return null; +}; + +const AppSidebar: React.FC = () => { + const navigate = useNavigate(); + const location = useLocation(); + const { canAccessPath, routers } = usePermission(); + + const visibleItems = React.useMemo( + () => buildMenuItems(Array.isArray(routers) ? routers : [], '/', canAccessPath), + [canAccessPath, routers], + ); + const selectedKey = React.useMemo( + () => findMatchedPath(visibleItems, location.pathname) ?? '/index', + [location.pathname, visibleItems], + ); + const openKeys = React.useMemo( + () => findParentKeys(visibleItems, selectedKey) ?? [], + [selectedKey, visibleItems], + ); + const [manualOpenKeys, setManualOpenKeys] = React.useState([]); + + React.useEffect(() => { + setManualOpenKeys((prev) => Array.from(new Set([...prev, ...openKeys]))); + }, [openKeys]); + + const onClick: MenuProps['onClick'] = (e) => { + navigate(e.key); + }; + + return ( + setManualOpenKeys(keys as string[])} + onClick={onClick} + items={visibleItems} + /> + ); +}; + +export default AppSidebar; diff --git a/src/contexts/PermissionContext.tsx b/src/contexts/PermissionContext.tsx new file mode 100644 index 0000000..21edabc --- /dev/null +++ b/src/contexts/PermissionContext.tsx @@ -0,0 +1,334 @@ +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { message } from 'antd'; +import { getInfo, getRouters, type RouterNode } from '@/api/permission'; +import { getUserProfile } from '@/api/user'; +import { getToken } from '@/utils/auth'; + +interface PermissionContextValue { + loading: boolean; + ready: boolean; + userName: string; + roles: string[]; + permissions: string[]; + routers: RouterNode[]; + refreshPermissions: () => Promise; + hasRole: (roles: string | string[]) => boolean; + hasPermi: (permissions: string | string[]) => boolean; + canAccessPath: (path: string) => boolean; +} + +const PermissionContext = createContext(null); + +const ALWAYS_ALLOW_PATHS = new Set([ + '/', + '/index', + '/profile', +]); +const ROUTE_ALIASES: Record = { + '/demandManage': ['/project/demandManage'], + '/workAppraisal/dashboard': ['/workAppraisal/taskModule'], + '/dashboard/project-execution': ['/projectBank/projectProgress'], + '/projectBank/projectProgress': ['/dashboard/project-execution'], + '/projectBank/projectUser': ['/project/detail'], + '/workAppraisal/myPerformance': ['/projectBank/userScore'], + '/projectBank/userScore': ['/workAppraisal/myPerformance'], + '/projectBank/userScoreDetail': ['/workAppraisal/myPerformance'], +}; +const SUPER_PERMI = '*:*:*'; +const ADMIN_ROLE = 'admin'; +const ADMIN_ROLE_ALIASES = ['admin', '超级管理员', 'superadmin']; + +const normalizePath = (rawPath: string) => { + const path = rawPath.split('?')[0]?.split('#')[0] ?? ''; + if (!path) { + return '/'; + } + const withLeadingSlash = path.startsWith('/') ? path : `/${path}`; + const normalized = withLeadingSlash.replace(/\/{2,}/g, '/'); + if (normalized.length > 1 && normalized.endsWith('/')) { + return normalized.slice(0, -1); + } + return normalized; +}; + +const joinPath = (base: string, child: string) => { + const normalizedBase = normalizePath(base); + if (!child) { + return normalizedBase; + } + if (child.startsWith('/')) { + return normalizePath(child); + } + if (normalizedBase === '/') { + return normalizePath(`/${child}`); + } + return normalizePath(`${normalizedBase}/${child}`); +}; + +const parseStringList = (input: unknown) => { + if (!Array.isArray(input)) { + return [] as string[]; + } + return input + .map((item) => { + if (typeof item === 'string') { + return item.trim(); + } + if (item && typeof item === 'object') { + const role = item as Record; + const roleName = role.roleKey ?? role.roleName ?? role.name ?? ''; + return String(roleName).trim(); + } + return ''; + }) + .filter(Boolean); +}; + +const extractRouteNodes = (payload: unknown): RouterNode[] => { + if (Array.isArray(payload)) { + return payload as RouterNode[]; + } + + if (payload && typeof payload === 'object') { + const obj = payload as Record; + const candidates = [obj.routes, obj.routers, obj.menus, obj.data, obj.children]; + + for (const candidate of candidates) { + if (Array.isArray(candidate)) { + return candidate as RouterNode[]; + } + if (candidate && typeof candidate === 'object') { + const nested = extractRouteNodes(candidate); + if (nested.length > 0) { + return nested; + } + } + } + } + + return []; +}; + +const isAdminRoleName = (role: string) => { + const roleText = role.trim().toLowerCase(); + return ADMIN_ROLE_ALIASES.some((alias) => roleText === alias.toLowerCase()); +}; + +const flattenRouterPaths = (routes: RouterNode[]) => { + const pathSet = new Set(); + + const walk = (nodes: RouterNode[], parentPath: string) => { + nodes.forEach((node) => { + const currentPath = joinPath(parentPath, String(node.path ?? '')); + if (currentPath && currentPath !== '/') { + pathSet.add(currentPath); + } + if (Array.isArray(node.children) && node.children.length > 0) { + walk(node.children, currentPath); + } + }); + }; + + walk(routes, '/'); + return pathSet; +}; + +const matchPathPattern = (allowedPath: string, actualPath: string) => { + if (allowedPath === actualPath) { + return true; + } + + if (allowedPath.includes(':')) { + const allowedParts = allowedPath.split('/').filter(Boolean); + const actualParts = actualPath.split('/').filter(Boolean); + if (allowedParts.length !== actualParts.length) { + return false; + } + return allowedParts.every((part, index) => part.startsWith(':') || part === actualParts[index]); + } + + return actualPath.startsWith(`${allowedPath}/`); +}; + +export const PermissionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [loading, setLoading] = useState(false); + const [ready, setReady] = useState(false); + const [userName, setUserName] = useState(''); + const [roles, setRoles] = useState([]); + const [permissions, setPermissions] = useState([]); + const [routers, setRouters] = useState([]); + const [allowedPaths, setAllowedPaths] = useState>(new Set()); + const [routeGuardEnabled, setRouteGuardEnabled] = useState(false); + + const clearPermissionState = useCallback(() => { + setUserName(''); + setRoles([]); + setPermissions([]); + setRouters([]); + setAllowedPaths(new Set()); + setRouteGuardEnabled(false); + }, []); + + const refreshPermissions = useCallback(async () => { + const token = getToken(); + if (!token) { + clearPermissionState(); + setReady(true); + setLoading(false); + return; + } + + setLoading(true); + setReady(false); + + let nextUserName = ''; + let nextRoles: string[] = []; + let nextPermissions: string[] = []; + let nextRouters: RouterNode[] = []; + let nextAllowedPaths = new Set(); + let nextRouteGuardEnabled = false; + + try { + try { + const info = await getInfo(); + nextRoles = parseStringList(info.roles); + nextPermissions = parseStringList(info.permissions); + nextUserName = String((info.user as Record | undefined)?.userName ?? ''); + } catch (error) { + // Fallback for environments where /getInfo is not available yet. + const profile = await getUserProfile(); + nextUserName = String(profile.user?.userName ?? ''); + const roleGroup = String((profile as Record).roleGroup ?? ''); + nextRoles = roleGroup + .split(',') + .map((role) => role.trim()) + .filter(Boolean); + } + + try { + const routersRaw = await getRouters(); + const routes = extractRouteNodes(routersRaw); + nextRouters = routes; + nextAllowedPaths = flattenRouterPaths(routes); + nextRouteGuardEnabled = true; + } catch (routerError) { + console.error('Failed to load router permission data:', routerError); + const isAdminUser = nextRoles.some((role) => isAdminRoleName(role)); + nextAllowedPaths = new Set(); + nextRouteGuardEnabled = !isAdminUser; + } + } catch (error) { + console.error('Failed to load permission data:', error); + message.error('加载权限信息失败'); + clearPermissionState(); + } finally { + setUserName(nextUserName); + setRoles(nextRoles); + setPermissions(nextPermissions); + setRouters(nextRouters); + setAllowedPaths(nextAllowedPaths); + setRouteGuardEnabled(nextRouteGuardEnabled); + setLoading(false); + setReady(true); + } + }, [clearPermissionState]); + + useEffect(() => { + void refreshPermissions(); + }, [refreshPermissions]); + + const roleSet = useMemo(() => new Set(roles), [roles]); + const permissionSet = useMemo(() => new Set(permissions), [permissions]); + + const isAdmin = useCallback( + () => roleSet.has(ADMIN_ROLE) || Array.from(roleSet).some((role) => isAdminRoleName(role)), + [roleSet], + ); + + const hasRole = useCallback( + (required: string | string[]) => { + if (isAdmin()) { + return true; + } + const targets = Array.isArray(required) ? required : [required]; + return targets.some((item) => roleSet.has(item)); + }, + [isAdmin, roleSet], + ); + + const hasPermi = useCallback( + (required: string | string[]) => { + if (isAdmin() || permissionSet.has(SUPER_PERMI)) { + return true; + } + const targets = Array.isArray(required) ? required : [required]; + return targets.some((item) => permissionSet.has(item)); + }, + [isAdmin, permissionSet], + ); + + const canAccessPath = useCallback( + (path: string) => { + const normalizedPath = normalizePath(path); + if (ALWAYS_ALLOW_PATHS.has(normalizedPath)) { + return true; + } + + if (isAdmin()) { + return true; + } + + if (!routeGuardEnabled) { + return false; + } + + if (allowedPaths.has(normalizedPath)) { + return true; + } + + const aliasTargets = ROUTE_ALIASES[normalizedPath] ?? []; + if (aliasTargets.some((aliasPath) => allowedPaths.has(aliasPath))) { + return true; + } + + if (normalizedPath === '/' && allowedPaths.has('/index')) { + return true; + } + + for (const item of allowedPaths) { + if (matchPathPattern(item, normalizedPath)) { + return true; + } + } + + return false; + }, + [allowedPaths, isAdmin, permissionSet, roleSet, routeGuardEnabled], + ); + + const value = useMemo( + () => ({ + loading, + ready, + userName, + roles, + permissions, + routers, + refreshPermissions, + hasRole, + hasPermi, + canAccessPath, + }), + [loading, ready, userName, roles, permissions, routers, refreshPermissions, hasRole, hasPermi, canAccessPath], + ); + + return {children}; +}; + +export const usePermission = () => { + const context = useContext(PermissionContext); + if (!context) { + throw new Error('usePermission must be used within PermissionProvider'); + } + return context; +}; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..a49f610 --- /dev/null +++ b/src/index.css @@ -0,0 +1,9 @@ +html, +body, +#root { + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} + diff --git a/src/layout/MainLayout.tsx b/src/layout/MainLayout.tsx new file mode 100644 index 0000000..6a628b4 --- /dev/null +++ b/src/layout/MainLayout.tsx @@ -0,0 +1,42 @@ +import React, { useState } from 'react'; +import { Layout, theme } from 'antd'; +import { Outlet } from 'react-router-dom'; +import AppSidebar from '../components/Sidebar/index'; +import AppNavbar from '../components/Navbar'; +import './layout.css'; + +const { Header, Sider, Content } = Layout; + +const MainLayout: React.FC = () => { + const [collapsed, setCollapsed] = useState(false); + const { + token: { colorBgContainer, borderRadiusLG }, + } = theme.useToken(); + + return ( + + +
+ + + +
+ setCollapsed(!collapsed)} /> +
+ + + +
+ + ); +}; + +export default MainLayout; diff --git a/src/layout/layout.css b/src/layout/layout.css new file mode 100644 index 0000000..1ad21ac --- /dev/null +++ b/src/layout/layout.css @@ -0,0 +1,10 @@ +.logo { + height: 32px; + margin: 16px; + background: rgba(255, 255, 255, 0.2); + border-radius: 6px; + color: white; + text-align: center; + line-height: 32px; + font-weight: bold; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..b87dea3 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import 'antd/dist/reset.css'; +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/pages/Home/home.css b/src/pages/Home/home.css new file mode 100644 index 0000000..81abac9 --- /dev/null +++ b/src/pages/Home/home.css @@ -0,0 +1,37 @@ +.home-container { + font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + color: #676a6c; + overflow-x: hidden; +} + +.home-container .notification { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid #eee; +} + +.home-container ul { + padding: 0; + margin: 0; + list-style-type: none; +} + +.home-container h4 { + margin-top: 0px; +} + +.home-container h2 { + margin-top: 10px; + font-size: 26px; + font-weight: 100; +} + +.home-container p { + margin-top: 10px; +} + +.home-container .ant-card { + margin-bottom: 20px; +} diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx new file mode 100644 index 0000000..418643a --- /dev/null +++ b/src/pages/Home/index.tsx @@ -0,0 +1,103 @@ +import { Row, Col, Typography, Button, Tag, Divider, Card, Collapse } from 'antd'; +import './home.css'; +import payImg from '../../assets/pay.png'; + +const { Title, Text, Link } = Typography; + +const HomePage = () => { + const version = "3.8.8"; // From original file + + const goTarget = (href: string) => { + window.open(href, "_blank"); + }; + + return ( +
+ + +
+ 领取阿里云通用云产品1888优惠券
+ + https://www.aliyun.com/minisite/goods?userCode=brki8iof + +
+ 领取腾讯云通用云产品2860优惠券
+ + https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console + +
+ 阿里云服务器折扣区 + >☛☛点我进入☚☚ +     腾讯云服务器秒杀区 + >☛☛点我进入☚☚ +
+ + 云产品通用红包,可叠加官网常规优惠使用。(仅限新用户) + +
+ +
+ + + + 若依后台管理框架 +

+ 一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统... +

+

当前版本: v{version}

+

¥免费开源

+

+ + +

+ + + 技术选型 + + +

后端技术

+
  • SpringBoot
  • Spring Security
  • JWT
  • MyBatis
  • ...
+ + +

前端技术

+
  • Vue
  • Vuex
  • Element-ui
  • Axios
  • ...
+ +
+ +
+ + + + +

官网:http://www.ruoyi.vip

+

QQ群:151450850

+
+ + + +
  • 菜单管理新增路由名称
  • 新增数据脱敏过滤注解
  • ...
  • + }, + { + key: '2', + label: 'v3.8.7 - 2023-12-08', + children:
    1. 操作日志记录部门名称
    2. ...
    + } + ]} /> +
    + + + + donate + 你可以请作者喝杯咖啡表示鼓励 + + +
    +
    + ); +}; + +export default HomePage; diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx new file mode 100644 index 0000000..f666923 --- /dev/null +++ b/src/pages/Login/index.tsx @@ -0,0 +1,131 @@ +import { useState, useEffect } from 'react'; +import { Form, Input, Button, Checkbox, message } from 'antd'; +import { UserOutlined, LockOutlined, SafetyOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import Cookies from 'js-cookie'; +import { login, getCodeImg } from '../../api/login'; +import { TokenKey } from '../../utils/auth'; +import type { LoginRequest } from '@/types/api'; +import './login.css'; + +interface LoginFormValues { + username: string; + password: string; + code?: string; + rememberMe?: boolean; +} + +const LoginPage = () => { + const [form] = Form.useForm(); + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); + const [codeUrl, setCodeUrl] = useState(''); + const [captchaEnabled, setCaptchaEnabled] = useState(true); + const [uuid, setUuid] = useState(''); + + const fetchCode = () => { + getCodeImg() + .then((res) => { + const { img, uuid: captchaUuid, captchaEnabled: enabled } = res; + const shouldShowCaptcha = enabled === undefined ? true : enabled; + setCaptchaEnabled(shouldShowCaptcha); + if (shouldShowCaptcha && img) { + setCodeUrl(`data:image/gif;base64,${img}`); + setUuid(captchaUuid); + } else { + setCodeUrl(''); + setUuid(''); + } + }) + .catch((error: unknown) => { + console.error('Failed to load captcha image:', error); + message.error('Failed to load captcha image, please refresh.'); + }); + }; + + useEffect(() => { + void fetchCode(); + }, []); + + const handleLogin = (values: LoginFormValues) => { + setLoading(true); + const data: LoginRequest = { ...values, uuid }; + login(data) + .then((res) => { + message.success('Login successful!'); + const tokenToSet = res.token ?? 'mock_token'; + Cookies.set(TokenKey, tokenToSet); + navigate('/'); + }) + .catch((error: unknown) => { + const errorMessage = error instanceof Error ? error.message : 'Login failed.'; + message.error(errorMessage); + if (captchaEnabled) { + void fetchCode(); + } + }) + .finally(() => { + setLoading(false); + }); + }; + + return ( +
    +
    +

    新光线平台

    + + + } placeholder="账号" /> + + + + } placeholder="密码" /> + + + {captchaEnabled && ( + + + } placeholder="验证码" /> + +
    + {codeUrl ? void fetchCode()} alt="Captcha" /> : null} +
    +
    + )} + + + + 记住密码 + + + + + + +
    +
    + unissense.tech +
    +
    + ); +}; + +export default LoginPage; diff --git a/src/pages/Login/login.css b/src/pages/Login/login.css new file mode 100644 index 0000000..1379a1e --- /dev/null +++ b/src/pages/Login/login.css @@ -0,0 +1,63 @@ +.login { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + width: 100vw; + background-image: url("../../assets/login-background.jpg"); + background-size: cover; +} + +.title { + margin: 0px auto 30px auto; + text-align: center; + color: #707070; + font-size: 24px; + font-weight: bold; +} + +.login-form { + border-radius: 6px; + background: #ffffff; + width: 400px; + padding: 25px 25px 5px 25px; +} + +.login-form .ant-input-affix-wrapper { + height: 40px; +} + +.login-form .ant-input-prefix { + margin-right: 8px; +} + +.login-tip { + font-size: 13px; + text-align: center; + color: #bfbfbf; +} + +.login-code { + width: 33%; + height: 40px; + float: right; +} + +.login-code img { + cursor: pointer; + vertical-align: middle; + height: 40px; +} + +.el-login-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial, sans-serif; + font-size: 12px; + letter-spacing: 1px; +} diff --git a/src/pages/Profile/ResetPassword.tsx b/src/pages/Profile/ResetPassword.tsx new file mode 100644 index 0000000..1de5735 --- /dev/null +++ b/src/pages/Profile/ResetPassword.tsx @@ -0,0 +1,69 @@ +import { Form, Input, Button, message } from 'antd'; +import { updateUserPwd } from '../../api/user'; + +interface ResetPasswordValues { + oldPassword: string; + newPassword: string; + confirmPassword: string; +} + +const ResetPassword = () => { + const [form] = Form.useForm(); + + const onFinish = (values: ResetPasswordValues) => { + updateUserPwd(values.oldPassword, values.newPassword).then(() => { + message.success('修改成功,请重新登录'); + form.resetFields(); + }).catch(() => { + message.error('修改失败'); + }); + }; + + return ( +
    + + + + + + + ({ + validator(_, value) { + if (!value || getFieldValue('newPassword') === value) { + return Promise.resolve(); + } + return Promise.reject(new Error('两次输入的密码不一致!')); + }, + }), + ]} + > + + + + + +
    + ); +}; + +export default ResetPassword; diff --git a/src/pages/Profile/UserInfo.tsx b/src/pages/Profile/UserInfo.tsx new file mode 100644 index 0000000..35c7dc7 --- /dev/null +++ b/src/pages/Profile/UserInfo.tsx @@ -0,0 +1,77 @@ +import { useEffect } from 'react'; +import { Form, Input, Button, Radio, message } from 'antd'; +import { updateUserProfile } from '../../api/user'; +import type { UpdateUserProfilePayload, UserProfileUser } from '@/types/api'; + +interface UserInfoProps { + user: UserProfileUser; +} + +const UserInfo = ({ user }: UserInfoProps) => { + const [form] = Form.useForm(); + + useEffect(() => { + // Set form fields when user data is available + form.setFieldsValue({ + nickName: user.nickName, + phonenumber: user.phonenumber, + email: user.email, + sex: user.sex, + }); + }, [user, form]); + + const onFinish = (values: UpdateUserProfilePayload) => { + updateUserProfile(values) + .then(() => { + message.success('修改成功'); + }) + .catch(() => { + message.error('修改失败'); + }); + }; + + return ( +
    + + + + + + + + + + + + + + + + + + +
    + ); +}; + +export default UserInfo; diff --git a/src/pages/Profile/index.tsx b/src/pages/Profile/index.tsx new file mode 100644 index 0000000..2ee0e68 --- /dev/null +++ b/src/pages/Profile/index.tsx @@ -0,0 +1,86 @@ +import { useState, useEffect } from 'react'; +import { Row, Col, Card, Tabs, Spin, List, message } from 'antd'; +import { UserOutlined, PhoneOutlined, MailOutlined, HomeOutlined, TeamOutlined, CalendarOutlined } from '@ant-design/icons'; +import { getUserProfile } from '../../api/user'; +import UserInfo from './UserInfo'; +import ResetPassword from './ResetPassword'; +import type { UserProfileUser } from '@/types/api'; +import './profile.css'; + +const { TabPane } = Tabs; + +const ProfilePage = () => { + const [user, setUser] = useState(null); + const [roleGroup, setRoleGroup] = useState(''); + const [postGroup, setPostGroup] = useState(''); + const [loading, setLoading] = useState(true); + + useEffect(() => { + getUserProfile() + .then((response) => { + setUser(response.user); + setRoleGroup(response.roleGroup); + setPostGroup(response.postGroup); + setLoading(false); + }) + .catch((error: unknown) => { + console.error('Failed to load user profile:', error); + message.error('Failed to load user profile.'); + setLoading(false); + }); + }, []); + + if (loading || !user) { + return ; + } + + const profileItems = [ + { icon: , label: '用户名称', value: user.userName ?? '' }, + { icon: , label: '手机号码', value: user.phonenumber ?? '' }, + { icon: , label: '用户邮箱', value: user.email ?? '' }, + { icon: , label: '所属部门', value: user.dept ? `${user.dept.deptName ?? ''} / ${postGroup}` : '' }, + { icon: , label: '所属角色', value: roleGroup }, + { icon: , label: '创建日期', value: user.createTime ?? '' }, + ]; + + return ( +
    + + + +
    + avatar +
    + ( + + {item.value}
    } + /> + + )} + /> + + + + + + + + + + + + + + + +
    + ); +}; + +export default ProfilePage; diff --git a/src/pages/Profile/profile.css b/src/pages/Profile/profile.css new file mode 100644 index 0000000..2ff1949 --- /dev/null +++ b/src/pages/Profile/profile.css @@ -0,0 +1,8 @@ +.profile-app-container .pull-right { + float: right; + color: #999; +} + +.profile-app-container .ant-list-item-meta-description { + width: 100%; +} diff --git a/src/pages/dashboard/ProjectExecutionPage.tsx b/src/pages/dashboard/ProjectExecutionPage.tsx new file mode 100644 index 0000000..f68b4e9 --- /dev/null +++ b/src/pages/dashboard/ProjectExecutionPage.tsx @@ -0,0 +1,787 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Button, DatePicker, Empty, Form, Input, message, Progress, Select, Space, Table, Tooltip } from 'antd'; +import type { TableColumnsType } from 'antd'; +import { + CalendarOutlined, + FieldTimeOutlined, + ProjectOutlined, + ReloadOutlined, + RiseOutlined, + SearchOutlined, + TeamOutlined, +} from '@ant-design/icons'; +import zhCN from 'antd/es/date-picker/locale/zh_CN'; +import dayjs, { type Dayjs } from 'dayjs'; +import { useNavigate } from 'react-router-dom'; +import { parseTime } from '@/utils/ruoyi'; +import { listProject } from '@/api/project'; +import { listProjectExecution } from '@/api/projectExecution'; +import { getDicts } from '@/api/system/dict'; +import './project-execution.css'; + +const { RangePicker } = DatePicker; + +// Back-end endpoint /projectBank/projectProgress/list may be unavailable in some environments. +// Keep it disabled by default to avoid 404 noise in browser/devtools. +const ENABLE_PROJECT_EXECUTION_API = false; +const PERIOD_SWITCH_DAYS = 62; + +interface BoardRow { + projectId?: string | number; + projectName?: string; + projectCode?: string; + projectLeaderName?: string; + startDate?: string; + endDate?: string; + projectState?: string; + teamNum?: number; + budgetDate?: number | string; + planProgress?: number; + actualProgress?: number; + progressDeviation?: number; + delayDays?: number; + riskLevel?: 'normal' | 'warning' | 'delay'; + [key: string]: unknown; +} + +interface QueryParams { + pageNum: number; + pageSize: number; + projectName?: string; + projectLeaderName?: string; + projectState?: string; + beginTime?: string; + endTime?: string; +} + +interface PeriodMeta { + key: string; + label: string; + subLabel: string; + start: Dayjs; + end: Dayjs; + current: boolean; +} + +interface PeriodMetric { + active: boolean; + planPercent: number; + actualPercent: number; + delay: boolean; +} + +const getDefaultRange = (): [Dayjs, Dayjs] => [dayjs().startOf('year'), dayjs().endOf('year')]; + +const toNumber = (value: unknown, fallback = 0) => { + const num = Number(value); + return Number.isFinite(num) ? num : fallback; +}; + +const clampPercent = (value: number) => { + if (!Number.isFinite(value)) { + return 0; + } + if (value < 0) return 0; + if (value > 100) return 100; + return Number(value.toFixed(1)); +}; + +const toDate = (value: unknown) => { + if (value === null || value === undefined || value === '') { + return null; + } + const parsed = dayjs(value as dayjs.ConfigType); + return parsed.isValid() ? parsed.startOf('day') : null; +}; + +const laterDate = (left: Dayjs, right: Dayjs) => (left.isAfter(right) ? left : right); + +const earlierDate = (left: Dayjs, right: Dayjs) => (left.isBefore(right) ? left : right); + +const calcPlanProgress = (startDate?: string, endDate?: string) => { + const start = toDate(startDate); + const end = toDate(endDate); + if (!start || !end || end.isBefore(start, 'day')) { + return 0; + } + + const totalDays = end.diff(start, 'day') + 1; + const today = dayjs().startOf('day'); + const elapsedDays = today.isBefore(start, 'day') ? 0 : today.diff(start, 'day') + 1; + return clampPercent((elapsedDays / Math.max(totalDays, 1)) * 100); +}; + +const inferActualProgress = (row: Record, planProgress: number) => { + const directFields = [ + 'actualProgress', + 'execProgress', + 'projectProgress', + 'progress', + 'finishRate', + 'completeRate', + ]; + for (const key of directFields) { + if (row[key] !== undefined && row[key] !== null && row[key] !== '') { + return clampPercent(toNumber(row[key], 0)); + } + } + + const rawState = String(row.projectState ?? '').toLowerCase(); + if (rawState === '2' || rawState.includes('完成')) return 100; + if (rawState === '0' || rawState.includes('待')) return 0; + if (rawState === '1' || rawState.includes('进行')) return clampPercent(planProgress * 0.9); + return clampPercent(planProgress * 0.75); +}; + +const calcDelayDays = (row: Record) => { + const end = toDate(row.endDate); + const rawState = String(row.projectState ?? '').toLowerCase(); + if (!end) { + return 0; + } + if (rawState === '2' || rawState.includes('完成')) { + return 0; + } + if (dayjs().isAfter(end, 'day')) { + return dayjs().startOf('day').diff(end, 'day'); + } + return 0; +}; + +const normalizeRows = (rows: unknown[]): BoardRow[] => { + return rows + .filter((item) => item && typeof item === 'object') + .map((item) => { + const row = item as Record; + const planProgress = clampPercent( + row.planProgress !== undefined + ? toNumber(row.planProgress) + : calcPlanProgress(String(row.startDate ?? ''), String(row.endDate ?? '')), + ); + const actualProgress = inferActualProgress(row, planProgress); + const progressDeviation = Number((actualProgress - planProgress).toFixed(1)); + const delayDays = row.delayDays !== undefined ? toNumber(row.delayDays) : calcDelayDays(row); + const riskLevel: BoardRow['riskLevel'] = + delayDays > 0 ? 'delay' : progressDeviation < -15 ? 'warning' : 'normal'; + + return { + ...(row as BoardRow), + projectId: row.projectId as string | number | undefined, + projectName: String(row.projectName ?? ''), + projectCode: String(row.projectCode ?? ''), + projectLeaderName: String(row.projectLeaderName ?? row.projectLeader ?? ''), + startDate: row.startDate ? String(row.startDate) : undefined, + endDate: row.endDate ? String(row.endDate) : undefined, + planProgress, + actualProgress, + progressDeviation, + delayDays, + riskLevel, + }; + }); +}; + +const extractRows = (response: unknown): { rows: unknown[]; total: number } => { + if (response && typeof response === 'object') { + const payload = response as Record; + if (Array.isArray(payload.rows)) { + return { rows: payload.rows, total: toNumber(payload.total, payload.rows.length) }; + } + if (payload.data && typeof payload.data === 'object') { + const dataObj = payload.data as Record; + if (Array.isArray(dataObj.rows)) { + return { rows: dataObj.rows, total: toNumber(dataObj.total, dataObj.rows.length) }; + } + } + } + if (Array.isArray(response)) { + return { rows: response, total: response.length }; + } + return { rows: [], total: 0 }; +}; + +const overlapDays = (rangeStart: Dayjs, rangeEnd: Dayjs, blockStart: Dayjs, blockEnd: Dayjs) => { + const start = laterDate(rangeStart, blockStart); + const end = earlierDate(rangeEnd, blockEnd); + if (end.isBefore(start, 'day')) { + return 0; + } + return end.diff(start, 'day') + 1; +}; + +const buildPeriods = (rangeStart: Dayjs, rangeEnd: Dayjs): PeriodMeta[] => { + if (!rangeStart.isValid() || !rangeEnd.isValid() || rangeEnd.isBefore(rangeStart, 'day')) { + return []; + } + + const periods: PeriodMeta[] = []; + const totalDays = rangeEnd.diff(rangeStart, 'day'); + const useWeekView = totalDays <= PERIOD_SWITCH_DAYS; + const today = dayjs(); + + if (useWeekView) { + let cursor = rangeStart.startOf('day'); + let index = 0; + while (!cursor.isAfter(rangeEnd, 'day')) { + const start = cursor; + const end = earlierDate(cursor.add(6, 'day').endOf('day'), rangeEnd.endOf('day')); + periods.push({ + key: `week-${index}`, + label: `${start.format('MM/DD')}-${end.format('MM/DD')}`, + subLabel: `${start.format('YYYY')} 第${index + 1}段`, + start, + end, + current: !today.isBefore(start.startOf('day')) && !today.isAfter(end.endOf('day')), + }); + cursor = end.add(1, 'day').startOf('day'); + index += 1; + } + return periods; + } + + let cursor = rangeStart.startOf('month'); + while (!cursor.isAfter(rangeEnd, 'day')) { + const start = laterDate(cursor.startOf('month'), rangeStart.startOf('day')); + const end = earlierDate(cursor.endOf('month'), rangeEnd.endOf('day')); + periods.push({ + key: cursor.format('YYYY-MM'), + label: cursor.format('MM月'), + subLabel: cursor.format('YYYY'), + start, + end, + current: !today.isBefore(start.startOf('day')) && !today.isAfter(end.endOf('day')), + }); + cursor = cursor.add(1, 'month').startOf('month'); + } + return periods; +}; + +const getPeriodMetric = (row: BoardRow, period: PeriodMeta): PeriodMetric => { + const start = toDate(row.startDate); + const end = toDate(row.endDate); + if (!start || !end || end.isBefore(start, 'day')) { + return { active: false, planPercent: 0, actualPercent: 0, delay: false }; + } + + const totalDays = end.diff(start, 'day') + 1; + const planDays = overlapDays(start, end, period.start, period.end); + const planPercent = clampPercent((planDays / totalDays) * 100); + const actualDaysTotal = Math.round((clampPercent(toNumber(row.actualProgress, 0)) / 100) * totalDays); + const actualEnd = actualDaysTotal > 0 ? start.add(actualDaysTotal - 1, 'day') : start.subtract(1, 'day'); + const actualDays = actualDaysTotal > 0 ? overlapDays(start, actualEnd, period.start, period.end) : 0; + const actualPercent = clampPercent((actualDays / totalDays) * 100); + + return { + active: planDays > 0, + planPercent, + actualPercent, + delay: toNumber(row.delayDays, 0) > 0 && period.current, + }; +}; + +const getStateTone = (label: string, riskLevel: BoardRow['riskLevel']) => { + if (riskLevel === 'delay') { + return 'is-delay'; + } + if (riskLevel === 'warning') { + return 'is-warning'; + } + if (label.includes('完成')) { + return 'is-finished'; + } + if (label.includes('进行')) { + return 'is-active'; + } + return 'is-pending'; +}; + +const getProgressTone = (deviation: number) => { + if (deviation < -15) { + return '#ff4d4f'; + } + if (deviation < 0) { + return '#fa8c16'; + } + return '#16a34a'; +}; + +const formatPercent = (value: number) => `${clampPercent(value)}%`; + +const ProjectExecutionPage = () => { + const [queryForm] = Form.useForm(); + const navigate = useNavigate(); + const defaultRange = useMemo(() => getDefaultRange(), []); + const [loading, setLoading] = useState(false); + const [statusOptions, setStatusOptions] = useState>([]); + const [rows, setRows] = useState([]); + const [total, setTotal] = useState(0); + const fallbackTipShownRef = useRef(false); + const [queryParams, setQueryParams] = useState({ + pageNum: 1, + pageSize: 10, + projectName: undefined, + projectLeaderName: undefined, + projectState: undefined, + beginTime: defaultRange[0].format('YYYY-MM-DD'), + endTime: defaultRange[1].format('YYYY-MM-DD'), + }); + + const rangeStart = useMemo( + () => toDate(queryParams.beginTime) ?? defaultRange[0], + [defaultRange, queryParams.beginTime], + ); + const rangeEnd = useMemo( + () => toDate(queryParams.endTime) ?? defaultRange[1], + [defaultRange, queryParams.endTime], + ); + const periods = useMemo(() => buildPeriods(rangeStart, rangeEnd), [rangeEnd, rangeStart]); + const statusMap = useMemo( + () => new Map(statusOptions.map((item) => [String(item.dictValue), item.dictLabel])), + [statusOptions], + ); + + const summary = useMemo(() => { + const totalCount = rows.length; + const executingCount = rows.filter((row) => { + const statusLabel = statusMap.get(String(row.projectState ?? '')) ?? String(row.projectState ?? ''); + return statusLabel.includes('进行') || String(row.projectState ?? '') === '1'; + }).length; + const delayCount = rows.filter((row) => toNumber(row.delayDays, 0) > 0).length; + const avgActual = rows.length + ? Number((rows.reduce((sum, row) => sum + toNumber(row.actualProgress), 0) / rows.length).toFixed(1)) + : 0; + const totalMembers = rows.reduce((sum, row) => sum + toNumber(row.teamNum, 0), 0); + + return { totalCount, executingCount, delayCount, avgActual, totalMembers }; + }, [rows, statusMap]); + + const fetchStatusDict = useCallback(async () => { + try { + const response = await getDicts('business_projectstate'); + const list = Array.isArray(response) + ? response + : response && typeof response === 'object' && Array.isArray((response as Record).data) + ? ((response as Record).data as Array<{ dictValue: string; dictLabel: string }>) + : []; + setStatusOptions(list); + } catch (error) { + console.error('Failed to fetch project state dict:', error); + setStatusOptions([]); + } + }, []); + + const fetchList = useCallback(async () => { + setLoading(true); + try { + let response: unknown; + let fallbackUsed = false; + if (ENABLE_PROJECT_EXECUTION_API) { + try { + response = await listProjectExecution(queryParams as unknown as Record); + } catch { + fallbackUsed = true; + response = await listProject(queryParams as unknown as Record); + } + } else { + fallbackUsed = true; + response = await listProject(queryParams as unknown as Record); + } + + const { rows: rawRows, total: rawTotal } = extractRows(response); + setRows(normalizeRows(rawRows)); + setTotal(rawTotal); + + if (ENABLE_PROJECT_EXECUTION_API && fallbackUsed && !fallbackTipShownRef.current) { + fallbackTipShownRef.current = true; + message.info('项目执行表接口未就绪,已自动使用项目列表估算执行进度'); + } + } catch (error) { + console.error('Failed to fetch project execution list:', error); + message.error('获取项目执行表失败'); + setRows([]); + setTotal(0); + } finally { + setLoading(false); + } + }, [queryParams]); + + useEffect(() => { + void fetchStatusDict(); + }, [fetchStatusDict]); + + useEffect(() => { + void fetchList(); + }, [fetchList]); + + const openProject = useCallback( + (row: BoardRow) => { + const params = new URLSearchParams(); + params.set('projectId', String(row.projectId ?? '')); + params.set('projectName', row.projectName ?? ''); + + const rowStart = toDate(row.startDate); + const rowEnd = toDate(row.endDate); + if (rowStart) { + params.set('startDate', String(rowStart.valueOf())); + } + if (rowEnd) { + params.set('endDate', String(rowEnd.endOf('day').valueOf())); + } + + navigate(`/projectBank/projectUser?${params.toString()}`); + }, + [navigate], + ); + + const handleQuery = () => { + const values = queryForm.getFieldsValue(); + const selectedRange = values.dateRange as [Dayjs, Dayjs] | undefined; + setQueryParams((prev) => ({ + ...prev, + pageNum: 1, + projectName: values.projectName ?? undefined, + projectLeaderName: values.projectLeaderName ?? undefined, + projectState: values.projectState ?? undefined, + beginTime: selectedRange?.[0]?.format('YYYY-MM-DD') ?? defaultRange[0].format('YYYY-MM-DD'), + endTime: selectedRange?.[1]?.format('YYYY-MM-DD') ?? defaultRange[1].format('YYYY-MM-DD'), + })); + }; + + const handleReset = () => { + const nextRange = getDefaultRange(); + queryForm.resetFields(); + queryForm.setFieldsValue({ dateRange: nextRange }); + setQueryParams({ + pageNum: 1, + pageSize: 10, + projectName: undefined, + projectLeaderName: undefined, + projectState: undefined, + beginTime: nextRange[0].format('YYYY-MM-DD'), + endTime: nextRange[1].format('YYYY-MM-DD'), + }); + }; + + const columns = useMemo>(() => { + const baseColumns: TableColumnsType = [ + { + title: '项目名称', + key: 'projectName', + dataIndex: 'projectName', + width: 260, + fixed: 'left', + render: (value: unknown, row) => ( +
    + +
    + {row.projectCode || '未配置项目编号'} + + {parseTime(row.startDate, 'YYYY-MM-DD') || '--'} 至 {parseTime(row.endDate, 'YYYY-MM-DD') || '--'} + +
    +
    + ), + }, + { + title: '负责人', + dataIndex: 'projectLeaderName', + width: 120, + fixed: 'left', + render: (value: unknown) => String(value ?? '-') || '-', + }, + { + title: '状态', + dataIndex: 'projectState', + width: 112, + fixed: 'left', + render: (value: unknown, row) => { + const label = statusMap.get(String(value ?? '')) ?? (String(value ?? '') || '未配置'); + return {label}; + }, + }, + { + title: '执行概览', + key: 'overview', + width: 240, + fixed: 'left', + render: (_value, row) => { + const plan = clampPercent(toNumber(row.planProgress, 0)); + const actual = clampPercent(toNumber(row.actualProgress, 0)); + const deviation = toNumber(row.progressDeviation, 0); + return ( +
    +
    + 总进度 + + {`${deviation > 0 ? '+' : ''}${deviation}%`} + +
    +
    +
    + 计划 + {formatPercent(plan)} +
    + +
    +
    +
    + 执行 + {formatPercent(actual)} +
    + +
    +
    + {`团队 ${toNumber(row.teamNum, 0)} 人`} + {`工时 ${String(row.budgetDate ?? '-')}`} +
    +
    + ); + }, + }, + ]; + + const periodColumns: TableColumnsType = periods.map((period) => ({ + title: ( +
    + {period.label} + {period.subLabel} +
    + ), + key: period.key, + width: 164, + className: period.current ? 'period-column-current' : undefined, + render: (_value, row) => { + const metric = getPeriodMetric(row, period); + if (!metric.active) { + return ( +
    + 无排期 +
    + ); + } + + return ( +
    +
    + 计划 {formatPercent(metric.planPercent)} + 执行 {formatPercent(metric.actualPercent)} +
    +
    +
    +
    +
    + + {metric.delay ? '当前节点延期' : metric.actualPercent >= metric.planPercent ? '执行平稳' : '待追赶'} + +
    + ); + }, + })); + + return [...baseColumns, ...periodColumns]; + }, [openProject, periods, statusMap]); + + const stats = useMemo( + () => [ + { + key: 'total', + label: '项目总数', + value: summary.totalCount, + extra: '纳入当前筛选范围', + icon: , + tone: 'blue', + }, + { + key: 'active', + label: '进行中项目', + value: summary.executingCount, + extra: `总成员 ${summary.totalMembers} 人`, + icon: , + tone: 'orange', + }, + { + key: 'delay', + label: '延期项目', + value: summary.delayCount, + extra: '需要重点跟踪', + icon: , + tone: 'red', + }, + { + key: 'avg', + label: '平均执行率', + value: `${summary.avgActual}%`, + extra: periods.length > 0 ? `${periods.length} 个阶段窗口` : '暂无阶段', + icon: , + tone: 'teal', + }, + ], + [periods.length, summary], + ); + + const scrollX = useMemo(() => 260 + 120 + 112 + 240 + periods.length * 164, [periods.length]); + const viewLabel = periods.length > 0 && rangeEnd.diff(rangeStart, 'day') <= PERIOD_SWITCH_DAYS ? '周视图' : '月视图'; + + return ( +
    +
    +
    +
    +
    +

    项目执行表

    +

    按项目维度对齐计划与实际执行,保留旧系统横向时间轴查看逻辑,并提升信息层次与可读性。

    +
    +
    + + {`${rangeStart.format('YYYY.MM.DD')} - ${rangeEnd.format('YYYY.MM.DD')}`} + {viewLabel} +
    +
    +
    + {stats.map((item) => ( +
    +
    {item.icon}
    +
    + {item.label} + {item.value} + {item.extra} +
    +
    + ))} +
    +
    +
    + +
    +
    + + + + + + + + + + + + + + + +
    + + + +
    + ); +}; + +export default CacheListPage; diff --git a/src/pages/monitor/CacheMonitorPage.tsx b/src/pages/monitor/CacheMonitorPage.tsx new file mode 100644 index 0000000..7eef7a8 --- /dev/null +++ b/src/pages/monitor/CacheMonitorPage.tsx @@ -0,0 +1,145 @@ +import { useState, useEffect } from 'react'; +import { Row, Col, Card, Descriptions, Spin, message, Typography } from 'antd'; +import ReactECharts from 'echarts-for-react'; +import * as echarts from 'echarts/core'; +import { macarons } from '../../themes/macarons'; +import { getCache } from '../../api/monitor/cache'; +import type { CacheMonitorResponse } from '@/types/api'; +import './cache-monitor.css'; + +const { Text } = Typography; + +echarts.registerTheme('macarons', macarons); + +const defaultCacheData: CacheMonitorResponse = { + info: {}, + dbSize: 0, + commandStats: [], +}; + +const toDisplayText = (value: string | number | undefined): string => { + if (value === undefined || value === null) { + return ''; + } + return String(value); +}; + +const CacheMonitorPage = () => { + const [loading, setLoading] = useState(true); + const [cacheData, setCacheData] = useState(defaultCacheData); + const [error, setError] = useState(null); + + useEffect(() => { + getCache() + .then((response) => { + setCacheData(response); + }) + .catch(() => { + const errorMsg = '加载缓存数据失败,请确认后端服务是否正常。'; + setError(errorMsg); + message.error(errorMsg); + }) + .finally(() => { + setLoading(false); + }); + }, []); + + if (loading) { + return ; + } + + if (error) { + return
    {error}
    ; + } + + const commandStatsOptions = { + tooltip: { + trigger: 'item', + formatter: '{a}
    {b} : {c} ({d}%)', + }, + series: [ + { + name: '命令', + type: 'pie', + roseType: 'radius', + radius: [15, 95], + center: ['50%', '38%'], + data: cacheData.commandStats || [], + animationEasing: 'cubicInOut', + animationDuration: 1000, + }, + ], + }; + + const usedMemoryOptions = { + tooltip: { + formatter: `{b}
    {a} : ${toDisplayText(cacheData.info?.used_memory_human)}`, + }, + series: [ + { + name: '峰值', + type: 'gauge', + min: 0, + max: 1000, + detail: { + formatter: toDisplayText(cacheData.info?.used_memory_human), + }, + data: [ + { + value: Number.parseFloat(toDisplayText(cacheData.info?.used_memory_human)) || 0, + name: '内存消耗', + }, + ], + }, + ], + }; + + return ( +
    + + + + + {toDisplayText(cacheData.info?.redis_version)} + + {cacheData.info?.redis_mode === 'standalone' ? '单机' : toDisplayText(cacheData.info?.redis_mode)} + + {toDisplayText(cacheData.info?.tcp_port)} + {toDisplayText(cacheData.info?.connected_clients)} + {toDisplayText(cacheData.info?.uptime_in_days)} + {toDisplayText(cacheData.info?.used_memory_human)} + + {cacheData.info?.used_cpu_user_children + ? Number.parseFloat(toDisplayText(cacheData.info.used_cpu_user_children)).toFixed(2) + : ''} + + {toDisplayText(cacheData.info?.maxmemory_human)} + + {cacheData.info?.aof_enabled === '0' ? '否' : (cacheData.info?.aof_enabled ? '是' : '')} + + {toDisplayText(cacheData.info?.rdb_last_bgsave_status)} + {cacheData.dbSize || ''} + + {cacheData.info?.instantaneous_input_kbps + ? `${cacheData.info.instantaneous_input_kbps}kps/${cacheData.info.instantaneous_output_kbps}kps` + : ''} + + + + + + + + + + + + + + + +
    + ); +}; + +export default CacheMonitorPage; diff --git a/src/pages/monitor/JobMonitorPage.tsx b/src/pages/monitor/JobMonitorPage.tsx new file mode 100644 index 0000000..27722a5 --- /dev/null +++ b/src/pages/monitor/JobMonitorPage.tsx @@ -0,0 +1,585 @@ +import { useCallback, useEffect, useState } from 'react'; +import type { Key } from 'react'; +import { + Table, + Form, + Input, + Select, + Button, + Dropdown, + Modal, + message, + Space, + Tag, + Switch, + Row, + Col, +} from 'antd'; +import { + SearchOutlined, + ReloadOutlined, + PlusOutlined, + EditOutlined, + DeleteOutlined, + DownloadOutlined, + SyncOutlined, + QuestionCircleOutlined, + DownOutlined, +} from '@ant-design/icons'; +import type { TableColumnsType, MenuProps } from 'antd'; +import { + listJob, + getJob, + delJob, + addJob, + updateJob, + runJob, + changeJobStatus, +} from '../../api/monitor/job'; +import { saveAs } from 'file-saver'; +import dayjs from 'dayjs'; +import type { JobQueryParams, JobRecord } from '@/types/api'; +import './job-monitor.css'; + +const sysJobGroupDict = [ + { value: 'DEFAULT', label: '默认' }, + { value: 'SYSTEM', label: '系统' }, +]; + +const sysJobStatusDict = [ + { value: '0', label: '正常' }, + { value: '1', label: '暂停' }, +]; + +const parseTime = (time?: string | number | Date, pattern = 'YYYY-MM-DD HH:mm:ss'): string => { + return time ? dayjs(time).format(pattern) : ''; +}; + +const escapeCsvCell = (value: unknown): string => { + const raw = value === undefined || value === null ? '' : String(value); + return `"${raw.replace(/"/g, '""')}"`; +}; + +const normalizeRowKey = (value: Key): string | number => { + return typeof value === 'bigint' ? value.toString() : value; +}; + +const JobMonitorPage = () => { + const [form] = Form.useForm(); + const [queryForm] = Form.useForm(); + const [jobList, setJobList] = useState([]); + const [loading, setLoading] = useState(false); + const [total, setTotal] = useState(0); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [modalVisible, setModalVisible] = useState(false); + const [modalTitle, setModalTitle] = useState(''); + const [detailModalVisible, setDetailModalVisible] = useState(false); + const [cronGenModalVisible, setCronGenModalVisible] = useState(false); + const [currentJobDetail, setCurrentJobDetail] = useState({}); + + const [queryParams, setQueryParams] = useState({ + pageNum: 1, + pageSize: 10, + jobName: undefined, + jobGroup: undefined, + status: undefined, + }); + + const getList = useCallback(async () => { + setLoading(true); + try { + const response = await listJob(queryParams); + setJobList(response.rows ?? []); + setTotal(Number(response.total ?? 0)); + } catch (error: unknown) { + console.error('Failed to fetch job list:', error); + message.error('获取定时任务列表失败'); + } finally { + setLoading(false); + } + }, [queryParams]); + + useEffect(() => { + void getList(); + }, [getList]); + + const handleQuery = () => { + const values = queryForm.getFieldsValue(); + setQueryParams((prev) => ({ ...prev, ...values, pageNum: 1 })); + }; + + const resetQuery = () => { + queryForm.resetFields(); + setQueryParams({ pageNum: 1, pageSize: 10, jobName: undefined, jobGroup: undefined, status: undefined }); + }; + + const handleSelectionChange = (selectedKeys: Key[]) => { + setSelectedRowKeys(selectedKeys); + }; + + const handleAdd = () => { + form.resetFields(); + setModalTitle('添加任务'); + setModalVisible(true); + setCurrentJobDetail({ misfirePolicy: '1', concurrent: '1', status: '0' }); + }; + + const handleUpdate = async (record?: JobRecord) => { + form.resetFields(); + const selectedKey = record?.jobId ?? selectedRowKeys[0]; + const jobId = selectedKey === undefined ? undefined : normalizeRowKey(selectedKey); + if (jobId === undefined) { + message.warning('请选择要修改的任务'); + return; + } + + try { + const response = await getJob(jobId); + setCurrentJobDetail(response); + form.setFieldsValue({ + ...response, + status: response.status?.toString(), + misfirePolicy: response.misfirePolicy?.toString(), + concurrent: response.concurrent?.toString(), + }); + setModalTitle('修改任务'); + setModalVisible(true); + } catch { + message.error('获取任务详情失败'); + } + }; + + const handleDelete = async (record?: JobRecord) => { + const rawIds = record?.jobId !== undefined ? [record.jobId] : selectedRowKeys; + const jobIds = rawIds.map((item) => normalizeRowKey(item)); + if (jobIds.length === 0) { + message.warning('请选择要删除的任务'); + return; + } + + Modal.confirm({ + title: '确认删除', + content: `是否确认删除定时任务编号为"${jobIds.join(',')}"的数据项?`, + onOk: async () => { + try { + await delJob(jobIds.join(',')); + message.success('删除成功'); + setSelectedRowKeys([]); + void getList(); + } catch { + message.error('删除失败'); + } + }, + }); + }; + + const handleStatusChange = async (record: JobRecord) => { + if (record.jobId === undefined) { + return; + } + + const originalStatus = String(record.status ?? '1'); + const newStatus = originalStatus === '0' ? '1' : '0'; + try { + await changeJobStatus(record.jobId, newStatus); + message.success('状态修改成功'); + void getList(); + } catch { + message.error('状态修改失败'); + setJobList((prev) => + prev.map((job) => + job.jobId === record.jobId + ? { + ...job, + status: originalStatus, + } + : job, + ), + ); + } + }; + + const handleRun = async (record: JobRecord) => { + if (record.jobId === undefined || !record.jobGroup) { + message.warning('当前任务信息不完整,无法执行'); + return; + } + const { jobId, jobGroup } = record; + + Modal.confirm({ + title: '确认执行', + content: `确认要立即执行一次"${record.jobName ?? ''}"任务吗?`, + onOk: async () => { + try { + await runJob(jobId, jobGroup); + message.success('执行成功'); + } catch { + message.error('执行失败'); + } + }, + }); + }; + + const handleExport = async () => { + const hide = message.loading('正在导出数据...', 0); + try { + const response = await listJob({ ...queryParams, pageNum: undefined, pageSize: undefined }); + const header = ['任务编号', '任务名称', '任务组名', '调用目标字符串', 'cron执行表达式', '状态']; + const rows = response.rows.map((job) => [ + job.jobId, + job.jobName, + jobGroupFormat(job), + job.invokeTarget, + job.cronExpression, + sysJobStatusDict.find((dict) => dict.value === String(job.status ?? ''))?.label ?? '', + ]); + + const csvContent = [header, ...rows] + .map((row) => row.map((cell) => escapeCsvCell(cell)).join(',')) + .join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + saveAs(blob, `job_${dayjs().format('YYYYMMDDHHmmss')}.csv`); + hide(); + message.success('导出成功'); + } catch { + hide(); + message.error('导出失败'); + } + }; + + const handleJobLog = (record?: JobRecord) => { + const jobId = record?.jobId ?? 0; + message.info(`跳转到任务日志页面 (Job ID: ${jobId})`); + }; + + const handleView = async (record: JobRecord) => { + if (record.jobId === undefined) { + message.warning('任务编号缺失'); + return; + } + + try { + const response = await getJob(record.jobId); + setCurrentJobDetail(response); + setDetailModalVisible(true); + } catch { + message.error('获取任务详细信息失败'); + } + }; + + const submitForm = async () => { + try { + const values = await form.validateFields(); + const jobData: JobRecord = { ...currentJobDetail, ...values }; + if (jobData.jobId !== undefined) { + await updateJob(jobData); + message.success('修改成功'); + } else { + await addJob(jobData); + message.success('新增成功'); + } + setModalVisible(false); + void getList(); + } catch (error: unknown) { + console.error('Submit job form failed:', error); + message.error('操作失败'); + } + }; + + const jobGroupFormat = (record: JobRecord): string => { + const value = String(record.jobGroup ?? ''); + return sysJobGroupDict.find((dict) => dict.value === value)?.label ?? value; + }; + + const columns: TableColumnsType = [ + Table.SELECTION_COLUMN, + { title: '任务编号', dataIndex: 'jobId', align: 'center', width: 100 }, + { title: '任务名称', dataIndex: 'jobName', align: 'center', ellipsis: true }, + { + title: '任务组名', + dataIndex: 'jobGroup', + align: 'center', + render: (_value, record) => jobGroupFormat(record), + }, + { title: '调用目标字符串', dataIndex: 'invokeTarget', align: 'center', ellipsis: true }, + { title: 'cron执行表达式', dataIndex: 'cronExpression', align: 'center', ellipsis: true }, + { + title: '状态', + dataIndex: 'status', + align: 'center', + render: (_value, record) => ( + void handleStatusChange(record)} + /> + ), + }, + { + title: '操作', + key: 'operation', + align: 'center', + width: 300, + render: (_value, record) => { + const moreItems: MenuProps['items'] = [ + { key: 'runOnce', label: '执行一次' }, + { key: 'taskDetail', label: '任务详情' }, + { key: 'scheduleTask', label: '调度任务' }, + ]; + + const handleMoreMenuClick: MenuProps['onClick'] = ({ key }) => { + if (key === 'runOnce') { + void handleRun(record); + return; + } + if (key === 'taskDetail') { + void handleView(record); + return; + } + if (key === 'scheduleTask') { + handleJobLog(record); + } + }; + + return ( + + + + + + + + ); + }, + }, + ]; + + return ( +
    +
    + + + + + + + + + + + + + +
    + + + + + + + + + + `共 ${count} 条`, + onChange: (page, pageSize) => { + setQueryParams((prev) => ({ ...prev, pageNum: page, pageSize })); + }, + }} + /> + + setModalVisible(false)} + width={800} + forceRender + > +
    + +
    + + + + + + + + + + + + + } + /> + + + + + + + + + + + {currentJobDetail.jobId !== undefined && ( + + + + + + )} + + + + + + + + + + + + + + + setCronGenModalVisible(false)} + footer={null} + width={800} + > +

    Cron Expression Generator component goes here.

    +
    + + setDetailModalVisible(false)} + footer={[]} + width={700} + > +
    + +
    + {currentJobDetail.jobId} + {currentJobDetail.jobName} + + + {jobGroupFormat(currentJobDetail)} + {parseTime(currentJobDetail.createTime)} + + + {currentJobDetail.cronExpression} + + + {parseTime(currentJobDetail.nextValidTime)} + + + {currentJobDetail.invokeTarget} + + + + {String(currentJobDetail.status ?? '') === '0' ? 正常 : 暂停} + + + + + {String(currentJobDetail.concurrent ?? '') === '0' ? 允许 : 禁止} + + + + + {String(currentJobDetail.misfirePolicy ?? '') === '0' + ? '默认策略' + : String(currentJobDetail.misfirePolicy ?? '') === '1' + ? '立即执行' + : String(currentJobDetail.misfirePolicy ?? '') === '2' + ? '执行一次' + : String(currentJobDetail.misfirePolicy ?? '') === '3' + ? '放弃执行' + : ''} + + + + + + + ); +}; + +export default JobMonitorPage; diff --git a/src/pages/monitor/LoginLogPage.tsx b/src/pages/monitor/LoginLogPage.tsx new file mode 100644 index 0000000..725badd --- /dev/null +++ b/src/pages/monitor/LoginLogPage.tsx @@ -0,0 +1,372 @@ +import { useCallback, useEffect, useState } from 'react'; +import type { Key } from 'react'; +import { + Table, + Form, + Input, + Select, + Button, + Modal, + message, + Space, + Tag, + DatePicker, +} from 'antd'; +import type { TableColumnsType, TableProps } from 'antd'; +import { + SearchOutlined, + ReloadOutlined, + DeleteOutlined, + DownloadOutlined, + UnlockOutlined, +} from '@ant-design/icons'; +import { + listLogininfor, + delLogininfor, + cleanLogininfor, + unlockLogininfor, +} from '../../api/monitor/logininfor'; +import { saveAs } from 'file-saver'; +import dayjs from 'dayjs'; +import type { LogininforQueryParams, LogininforRecord } from '@/types/api'; + +const { RangePicker } = DatePicker; + +type DateRange = [dayjs.Dayjs | null, dayjs.Dayjs | null] | null; + +type SortDirection = 'ascending' | 'descending'; + +const sysCommonStatusDict = [ + { value: '0', label: '正常', type: 'success' }, + { value: '1', label: '失败', type: 'error' }, +] as const; + +const defaultQueryParams: LogininforQueryParams = { + pageNum: 1, + pageSize: 10, + ipaddr: undefined, + userName: undefined, + status: undefined, + orderByColumn: 'loginTime', + isAsc: 'descending', +}; + +const parseTime = (time?: string | number | Date, pattern = 'YYYY-MM-DD HH:mm:ss'): string => { + return time ? dayjs(time).format(pattern) : ''; +}; + +const escapeCsvCell = (value: unknown): string => { + const raw = value === undefined || value === null ? '' : String(value); + return `"${raw.replace(/"/g, '""')}"`; +}; + +const LoginLogPage = () => { + const [queryForm] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [list, setList] = useState([]); + const [total, setTotal] = useState(0); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [dateRange, setDateRange] = useState(null); + const [queryParams, setQueryParams] = useState(defaultQueryParams); + + const getList = useCallback(async () => { + setLoading(true); + try { + const formattedQueryParams: LogininforQueryParams = { ...queryParams }; + if (dateRange?.[0] && dateRange?.[1]) { + formattedQueryParams.beginTime = dateRange[0].format('YYYY-MM-DD HH:mm:ss'); + formattedQueryParams.endTime = dateRange[1].format('YYYY-MM-DD HH:mm:ss'); + } else { + formattedQueryParams.beginTime = undefined; + formattedQueryParams.endTime = undefined; + } + + const response = await listLogininfor(formattedQueryParams); + setList(response.rows ?? []); + setTotal(Number(response.total ?? 0)); + } catch (error: unknown) { + console.error('Failed to fetch login logs:', error); + message.error('获取登录日志列表失败'); + } finally { + setLoading(false); + } + }, [dateRange, queryParams]); + + useEffect(() => { + void getList(); + }, [getList]); + + const handleQuery = () => { + const values = queryForm.getFieldsValue(); + setQueryParams((prev) => ({ ...prev, ...values, pageNum: 1 })); + }; + + const resetQuery = () => { + queryForm.resetFields(); + setDateRange(null); + setQueryParams(defaultQueryParams); + }; + + const handleSelectionChange = (selectedKeys: Key[], rows: LogininforRecord[]) => { + setSelectedRowKeys(selectedKeys); + setSelectedRows(rows); + }; + + const handleDelete = async () => { + const infoIds = selectedRowKeys.map((key) => String(key)).join(','); + if (infoIds.length === 0) { + message.warning('请选择要删除的登录日志'); + return; + } + + Modal.confirm({ + title: '确认删除', + content: `是否确认删除访问编号为"${infoIds}"的数据项?`, + onOk: async () => { + try { + await delLogininfor(infoIds); + message.success('删除成功'); + setSelectedRowKeys([]); + setSelectedRows([]); + void getList(); + } catch { + message.error('删除失败'); + } + }, + }); + }; + + const handleClean = async () => { + Modal.confirm({ + title: '确认清空', + content: '是否确认清空所有登录日志数据项?', + onOk: async () => { + try { + await cleanLogininfor(); + message.success('清空成功'); + setSelectedRowKeys([]); + setSelectedRows([]); + void getList(); + } catch { + message.error('清空失败'); + } + }, + }); + }; + + const handleUnlock = async () => { + const userNames = selectedRows + .map((row) => row.userName) + .filter((name): name is string => typeof name === 'string' && name.length > 0); + + if (userNames.length !== 1) { + message.warning('请选择一个用户进行解锁'); + return; + } + + const targetUserName = userNames[0]; + Modal.confirm({ + title: '确认解锁', + content: `是否确认解锁用户"${targetUserName}"的登录状态?`, + onOk: async () => { + try { + await unlockLogininfor(targetUserName); + message.success(`用户"${targetUserName}"解锁成功`); + void getList(); + } catch { + message.error('解锁失败'); + } + }, + }); + }; + + const handleExport = async () => { + const hide = message.loading('正在导出数据...', 0); + try { + const response = await listLogininfor({ + ...queryParams, + pageNum: undefined, + pageSize: undefined, + }); + + const header = [ + '访问编号', + '用户名称', + '登录地址', + '登录地点', + '浏览器', + '操作系统', + '登录状态', + '操作信息', + '登录日期', + ]; + + const data = response.rows.map((log) => [ + log.infoId, + log.userName, + log.ipaddr, + log.loginLocation, + log.browser, + log.os, + sysCommonStatusDict.find((item) => item.value === String(log.status ?? ''))?.label ?? '', + log.msg, + parseTime(log.loginTime), + ]); + + const csvContent = [header, ...data] + .map((row) => row.map((cell) => escapeCsvCell(cell)).join(',')) + .join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + saveAs(blob, `logininfor_${dayjs().format('YYYYMMDDHHmmss')}.csv`); + hide(); + message.success('导出成功'); + } catch { + hide(); + message.error('导出失败'); + } + }; + + const handleTableChange: TableProps['onChange'] = (pagination, _filters, sorter) => { + const activeSorter = Array.isArray(sorter) ? sorter[0] : sorter; + const orderByColumn = + typeof activeSorter?.field === 'string' && activeSorter.field.length > 0 + ? activeSorter.field + : 'loginTime'; + + const isAsc: SortDirection = activeSorter?.order === 'ascend' ? 'ascending' : 'descending'; + + setQueryParams((prev) => ({ + ...prev, + orderByColumn, + isAsc, + pageNum: pagination.current ?? 1, + pageSize: pagination.pageSize ?? 10, + })); + }; + + const statusFormat = (status?: string) => { + const dict = sysCommonStatusDict.find((item) => item.value === String(status ?? '')); + return dict ? {dict.label} : String(status ?? ''); + }; + + const columns: TableColumnsType = [ + Table.SELECTION_COLUMN, + { title: '访问编号', dataIndex: 'infoId', align: 'center', width: 90 }, + { title: '用户名称', dataIndex: 'userName', align: 'center', ellipsis: true, sorter: true }, + { title: '登录地址', dataIndex: 'ipaddr', align: 'center', ellipsis: true }, + { title: '登录地点', dataIndex: 'loginLocation', align: 'center', ellipsis: true }, + { title: '浏览器', dataIndex: 'browser', align: 'center', ellipsis: true }, + { title: '操作系统', dataIndex: 'os', align: 'center', ellipsis: true }, + { + title: '登录状态', + dataIndex: 'status', + align: 'center', + render: (text) => statusFormat(typeof text === 'string' ? text : undefined), + }, + { title: '操作信息', dataIndex: 'msg', align: 'center', ellipsis: true }, + { + title: '登录日期', + dataIndex: 'loginTime', + align: 'center', + width: 180, + sorter: true, + render: (text) => parseTime(typeof text === 'string' ? text : undefined), + }, + ]; + + return ( +
    +
    + + + + + + + + + + + setDateRange(dates ? [dates[0], dates[1]] : null)} + showTime={{ + defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')], + }} + format="YYYY-MM-DD HH:mm:ss" + style={{ width: 240 }} + /> + + + + + + + + + + + + + + +
    `共 ${count} 条`, + onChange: (page, pageSize) => { + setQueryParams((prev) => ({ ...prev, pageNum: page, pageSize })); + }, + }} + onChange={handleTableChange} + /> + + ); +}; + +export default LoginLogPage; diff --git a/src/pages/monitor/OnlineUserPage.tsx b/src/pages/monitor/OnlineUserPage.tsx new file mode 100644 index 0000000..83b3d92 --- /dev/null +++ b/src/pages/monitor/OnlineUserPage.tsx @@ -0,0 +1,167 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Table, Form, Input, Button, Modal, message } from 'antd'; +import type { TableColumnsType } from 'antd'; +import { SearchOutlined, ReloadOutlined, DeleteOutlined } from '@ant-design/icons'; +import { listOnline, forceLogout } from '../../api/monitor/online'; +import dayjs from 'dayjs'; +import type { OnlineQueryParams, OnlineRecord } from '@/types/api'; + +const defaultQueryParams: OnlineQueryParams = { + pageNum: 1, + pageSize: 10, + ipaddr: undefined, + userName: undefined, +}; + +const parseTime = (time?: string | number | Date, pattern = 'YYYY-MM-DD HH:mm:ss'): string => { + return time ? dayjs(time).format(pattern) : ''; +}; + +const normalizeDateValue = (value: unknown): string | number | Date | undefined => { + if (typeof value === 'string' || typeof value === 'number' || value instanceof Date) { + return value; + } + if (dayjs.isDayjs(value)) { + return value.toDate(); + } + return undefined; +}; + +const OnlineUserPage = () => { + const [queryForm] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [list, setList] = useState([]); + const [total, setTotal] = useState(0); + + const [queryParams, setQueryParams] = useState(defaultQueryParams); + + const getList = useCallback(async () => { + setLoading(true); + try { + const response = await listOnline(queryParams); + setList(response.rows ?? []); + setTotal(Number(response.total ?? 0)); + } catch (error: unknown) { + console.error('Failed to fetch online users:', error); + message.error('获取在线用户列表失败'); + } finally { + setLoading(false); + } + }, [queryParams]); + + useEffect(() => { + void getList(); + }, [getList]); + + const handleQuery = () => { + const values = queryForm.getFieldsValue(); + setQueryParams((prev) => ({ ...prev, ...values, pageNum: 1 })); + }; + + const resetQuery = () => { + queryForm.resetFields(); + setQueryParams(defaultQueryParams); + }; + + const handleForceLogout = (row: OnlineRecord) => { + if (!row.tokenId) { + message.warning('当前会话缺少 tokenId,无法强退'); + return; + } + const tokenId = row.tokenId; + + Modal.confirm({ + title: '确认强退', + content: `是否确认强退名称为"${row.userName ?? ''}"的用户?`, + onOk: async () => { + try { + await forceLogout(tokenId); + message.success('强退成功'); + void getList(); + } catch { + message.error('强退失败'); + } + }, + }); + }; + + const columns: TableColumnsType = [ + { + title: '序号', + key: 'serial', + align: 'center', + width: 60, + render: (_value, _record, index) => { + const pageNum = queryParams.pageNum ?? 1; + const pageSize = queryParams.pageSize ?? 10; + return (pageNum - 1) * pageSize + index + 1; + }, + }, + { title: '会话编号', dataIndex: 'tokenId', align: 'center', ellipsis: true }, + { title: '登录名称', dataIndex: 'userName', align: 'center', ellipsis: true }, + { title: '部门名称', dataIndex: 'deptName', align: 'center', ellipsis: true }, + { title: '主机', dataIndex: 'ipaddr', align: 'center', ellipsis: true }, + { title: '登录地点', dataIndex: 'loginLocation', align: 'center', ellipsis: true }, + { title: '浏览器', dataIndex: 'browser', align: 'center', ellipsis: true }, + { title: '操作系统', dataIndex: 'os', align: 'center', ellipsis: true }, + { + title: '登录时间', + dataIndex: 'loginTime', + align: 'center', + width: 180, + render: (_text, record) => parseTime(normalizeDateValue(record.loginTime)), + }, + { + title: '操作', + key: 'operation', + align: 'center', + width: 80, + render: (_value, record) => ( + + ), + }, + ]; + + return ( +
    +
    + + + + + + + + + + + + +
    `共 ${count} 条`, + onChange: (page, pageSize) => { + setQueryParams((prev) => ({ ...prev, pageNum: page, pageSize })); + }, + }} + /> + + ); +}; + +export default OnlineUserPage; diff --git a/src/pages/monitor/OperationLogPage.tsx b/src/pages/monitor/OperationLogPage.tsx new file mode 100644 index 0000000..f6c355d --- /dev/null +++ b/src/pages/monitor/OperationLogPage.tsx @@ -0,0 +1,463 @@ +import { useCallback, useEffect, useState } from 'react'; +import type { Key } from 'react'; +import { + Table, + Form, + Input, + Select, + Button, + Modal, + message, + Space, + Tag, + DatePicker, + Descriptions, +} from 'antd'; +import type { TableColumnsType, TableProps } from 'antd'; +import { + SearchOutlined, + ReloadOutlined, + DeleteOutlined, + DownloadOutlined, + EyeOutlined, +} from '@ant-design/icons'; +import { listOperlog, delOperlog, cleanOperlog } from '../../api/monitor/operlog'; +import { saveAs } from 'file-saver'; +import dayjs from 'dayjs'; +import type { OperlogQueryParams, OperlogRecord } from '@/types/api'; + +const { RangePicker } = DatePicker; + +type DateRange = [dayjs.Dayjs | null, dayjs.Dayjs | null] | null; + +type SortDirection = 'ascending' | 'descending'; + +const sysOperTypeDict = [ + { value: '0', label: '其它', color: 'default' }, + { value: '1', label: '新增', color: 'success' }, + { value: '2', label: '修改', color: 'warning' }, + { value: '3', label: '删除', color: 'red' }, + { value: '4', label: '授权', color: 'processing' }, + { value: '5', label: '导出', color: 'processing' }, + { value: '6', label: '导入', color: 'processing' }, + { value: '7', label: '强退', color: 'red' }, + { value: '8', label: '生成代码', color: 'processing' }, + { value: '9', label: '清空数据', color: 'red' }, +] as const; + +const sysCommonStatusDict = [ + { value: '0', label: '正常', color: 'success' }, + { value: '1', label: '异常', color: 'error' }, +] as const; + +const defaultQueryParams: OperlogQueryParams = { + pageNum: 1, + pageSize: 10, + operIp: undefined, + title: undefined, + operName: undefined, + businessType: undefined, + status: undefined, + orderByColumn: 'operTime', + isAsc: 'descending', +}; + +const parseTime = (time?: string | number | Date, pattern = 'YYYY-MM-DD HH:mm:ss'): string => { + return time ? dayjs(time).format(pattern) : ''; +}; + +const normalizeDateValue = (value: unknown): string | number | Date | undefined => { + if (typeof value === 'string' || typeof value === 'number' || value instanceof Date) { + return value; + } + if (dayjs.isDayjs(value)) { + return value.toDate(); + } + return undefined; +}; + +const escapeCsvCell = (value: unknown): string => { + const raw = value === undefined || value === null ? '' : String(value); + return `"${raw.replace(/"/g, '""')}"`; +}; + +const OperationLogPage = () => { + const [queryForm] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [list, setList] = useState([]); + const [total, setTotal] = useState(0); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [dateRange, setDateRange] = useState(null); + const [detailModalVisible, setDetailModalVisible] = useState(false); + const [currentOperlogDetail, setCurrentOperlogDetail] = useState({}); + const [queryParams, setQueryParams] = useState(defaultQueryParams); + + const getList = useCallback(async () => { + setLoading(true); + try { + const formattedQueryParams: OperlogQueryParams = { ...queryParams }; + if (dateRange?.[0] && dateRange?.[1]) { + formattedQueryParams.beginTime = dateRange[0].format('YYYY-MM-DD HH:mm:ss'); + formattedQueryParams.endTime = dateRange[1].format('YYYY-MM-DD HH:mm:ss'); + } else { + formattedQueryParams.beginTime = undefined; + formattedQueryParams.endTime = undefined; + } + + const response = await listOperlog(formattedQueryParams); + setList(response.rows ?? []); + setTotal(Number(response.total ?? 0)); + } catch (error: unknown) { + console.error('Failed to fetch operation logs:', error); + message.error('获取操作日志列表失败'); + } finally { + setLoading(false); + } + }, [dateRange, queryParams]); + + useEffect(() => { + void getList(); + }, [getList]); + + const handleQuery = () => { + const values = queryForm.getFieldsValue(); + setQueryParams((prev) => ({ ...prev, ...values, pageNum: 1 })); + }; + + const resetQuery = () => { + queryForm.resetFields(); + setDateRange(null); + setQueryParams(defaultQueryParams); + }; + + const handleSelectionChange = (selectedKeys: Key[]) => { + setSelectedRowKeys(selectedKeys); + }; + + const handleDelete = async () => { + const operIds = selectedRowKeys.map((key) => String(key)).join(','); + if (!operIds) { + message.warning('请选择要删除的操作日志'); + return; + } + + Modal.confirm({ + title: '确认删除', + content: `是否确认删除日志编号为"${operIds}"的数据项?`, + onOk: async () => { + try { + await delOperlog(operIds); + message.success('删除成功'); + setSelectedRowKeys([]); + void getList(); + } catch { + message.error('删除失败'); + } + }, + }); + }; + + const handleClean = async () => { + Modal.confirm({ + title: '确认清空', + content: '是否确认清空所有操作日志数据项?', + onOk: async () => { + try { + await cleanOperlog(); + message.success('清空成功'); + setSelectedRowKeys([]); + void getList(); + } catch { + message.error('清空失败'); + } + }, + }); + }; + + const operTypeLabel = (type?: string): string => { + const dict = sysOperTypeDict.find((item) => item.value === String(type ?? '')); + return dict?.label ?? String(type ?? ''); + }; + + const operStatusLabel = (status?: string): string => { + const dict = sysCommonStatusDict.find((item) => item.value === String(status ?? '')); + return dict?.label ?? String(status ?? ''); + }; + + const handleExport = async () => { + const hide = message.loading('正在导出数据...', 0); + try { + const response = await listOperlog({ + ...queryParams, + pageNum: undefined, + pageSize: undefined, + }); + + const header = [ + '日志编号', + '系统模块', + '操作类型', + '操作人员', + '操作地址', + '操作地点', + '操作状态', + '操作日期', + '消耗时间', + '请求方式', + '请求地址', + ]; + + const data = response.rows.map((log) => [ + log.operId, + log.title, + operTypeLabel(typeof log.businessType === 'string' ? log.businessType : undefined), + log.operName, + log.operIp, + log.operLocation, + operStatusLabel(typeof log.status === 'string' ? log.status : undefined), + parseTime(normalizeDateValue(log.operTime)), + log.costTime ? `${log.costTime}毫秒` : '', + log.requestMethod, + log.operUrl, + ]); + + const csvContent = [header, ...data] + .map((row) => row.map((cell) => escapeCsvCell(cell)).join(',')) + .join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + saveAs(blob, `operlog_${dayjs().format('YYYYMMDDHHmmss')}.csv`); + hide(); + message.success('导出成功'); + } catch { + hide(); + message.error('导出失败'); + } + }; + + const handleView = (record: OperlogRecord) => { + setCurrentOperlogDetail(record); + setDetailModalVisible(true); + }; + + const operTypeFormat = (type?: string) => { + const dict = sysOperTypeDict.find((item) => item.value === String(type ?? '')); + return dict ? {dict.label} : String(type ?? ''); + }; + + const operStatusFormat = (status?: string) => { + const dict = sysCommonStatusDict.find((item) => item.value === String(status ?? '')); + return dict ? {dict.label} : String(status ?? ''); + }; + + const handleTableChange: TableProps['onChange'] = (pagination, _filters, sorter) => { + const activeSorter = Array.isArray(sorter) ? sorter[0] : sorter; + const orderByColumn = + typeof activeSorter?.field === 'string' && activeSorter.field.length > 0 + ? activeSorter.field + : 'operTime'; + const isAsc: SortDirection = activeSorter?.order === 'ascend' ? 'ascending' : 'descending'; + + setQueryParams((prev) => ({ + ...prev, + orderByColumn, + isAsc, + pageNum: pagination.current ?? 1, + pageSize: pagination.pageSize ?? 10, + })); + }; + + const columns: TableColumnsType = [ + Table.SELECTION_COLUMN, + { title: '日志编号', dataIndex: 'operId', align: 'center', width: 90 }, + { title: '系统模块', dataIndex: 'title', align: 'center', ellipsis: true }, + { + title: '操作类型', + dataIndex: 'businessType', + align: 'center', + render: (text) => operTypeFormat(typeof text === 'string' ? text : undefined), + }, + { title: '操作人员', dataIndex: 'operName', align: 'center', ellipsis: true, sorter: true }, + { title: '操作地址', dataIndex: 'operIp', align: 'center', ellipsis: true }, + { title: '操作地点', dataIndex: 'operLocation', align: 'center', ellipsis: true }, + { + title: '操作状态', + dataIndex: 'status', + align: 'center', + render: (text) => operStatusFormat(typeof text === 'string' ? text : undefined), + }, + { + title: '操作日期', + dataIndex: 'operTime', + align: 'center', + width: 180, + sorter: true, + render: (text) => parseTime(normalizeDateValue(text)), + }, + { + title: '消耗时间', + dataIndex: 'costTime', + align: 'center', + width: 100, + sorter: true, + render: (text) => (typeof text === 'number' && text > 0 ? `${text}毫秒` : ''), + }, + { + title: '操作', + key: 'operation', + align: 'center', + width: 80, + render: (_text, record) => ( + + ), + }, + ]; + + return ( +
    +
    + + + + + + + + + + + + + + + + + setDateRange(dates ? [dates[0], dates[1]] : null)} + showTime={{ + defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')], + }} + format="YYYY-MM-DD HH:mm:ss" + style={{ width: 240 }} + /> + + + + + + + + + + + + + +
    `共 ${count} 条`, + onChange: (page, pageSize) => { + setQueryParams((prev) => ({ ...prev, pageNum: page, pageSize })); + }, + }} + onChange={handleTableChange} + /> + + setDetailModalVisible(false)} + footer={[ + , + ]} + width={800} + > + + + {currentOperlogDetail.title} /{' '} + {operTypeFormat( + typeof currentOperlogDetail.businessType === 'string' ? currentOperlogDetail.businessType : undefined, + )} + + + {currentOperlogDetail.operName} / {currentOperlogDetail.operIp} / {currentOperlogDetail.operLocation} + + {currentOperlogDetail.operUrl} + {currentOperlogDetail.requestMethod} + + {currentOperlogDetail.method} + + + {currentOperlogDetail.operParam} + + + {currentOperlogDetail.jsonResult} + + + {operStatusFormat(typeof currentOperlogDetail.status === 'string' ? currentOperlogDetail.status : undefined)} + + + {typeof currentOperlogDetail.costTime === 'number' ? `${currentOperlogDetail.costTime}毫秒` : ''} + + + {parseTime(normalizeDateValue(currentOperlogDetail.operTime))} + + {String(currentOperlogDetail.status ?? '') === '1' && ( + + {currentOperlogDetail.errorMsg} + + )} + + + + ); +}; + +export default OperationLogPage; diff --git a/src/pages/monitor/ServerMonitorPage.tsx b/src/pages/monitor/ServerMonitorPage.tsx new file mode 100644 index 0000000..43185c3 --- /dev/null +++ b/src/pages/monitor/ServerMonitorPage.tsx @@ -0,0 +1,185 @@ +import { useEffect, useState } from 'react'; +import { Card, Col, Row, message, Typography, Descriptions, Progress, Table, Space } from 'antd'; +import type { TableColumnsType } from 'antd'; +import { + SettingOutlined, + FileTextOutlined, + DesktopOutlined, + CoffeeOutlined, + HddOutlined, +} from '@ant-design/icons'; +import dayjs from 'dayjs'; +import { getServerInfo } from '../../api/monitor/server'; +import type { ServerDiskInfo, ServerInfoResponse } from '@/types/api'; +import './server-monitor.css'; + +const { Text } = Typography; + +const defaultServerInfo: ServerInfoResponse = { + cpu: {}, + mem: {}, + jvm: {}, + sys: {}, + sysFiles: [], +}; + +const parseTime = (time?: string | number | Date, pattern = 'YYYY-MM-DD HH:mm:ss'): string => { + return time ? dayjs(time).format(pattern) : ''; +}; + +const toNumber = (value: unknown): number => { + if (typeof value === 'number') { + return Number.isFinite(value) ? value : 0; + } + if (typeof value === 'string') { + const parsed = Number.parseFloat(value); + return Number.isFinite(parsed) ? parsed : 0; + } + return 0; +}; + +const renderUsageProgress = (usage: unknown) => { + const percent = Math.floor(toNumber(usage)); + let status: 'normal' | 'exception' | 'active' | 'success' = 'normal'; + if (percent > 80) { + status = 'exception'; + } else if (percent > 60) { + status = 'active'; + } + return ; +}; + +const ServerMonitorPage = () => { + const [serverInfo, setServerInfo] = useState(defaultServerInfo); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const getList = async () => { + setLoading(true); + const hide = message.loading('正在加载服务监控数据,请稍候!', 0); + try { + const response = await getServerInfo(); + setServerInfo(response); + } catch (error: unknown) { + console.error('Failed to fetch server info:', error); + message.error('获取服务监控数据失败'); + } finally { + setLoading(false); + hide(); + } + }; + + void getList(); + }, []); + + const diskColumns: TableColumnsType = [ + { title: '盘符路径', dataIndex: 'dirName', align: 'center' }, + { title: '文件系统', dataIndex: 'sysTypeName', align: 'center' }, + { title: '盘符类型', dataIndex: 'typeName', align: 'center' }, + { title: '总大小', dataIndex: 'total', align: 'center' }, + { title: '可用大小', dataIndex: 'free', align: 'center' }, + { title: '已用大小', dataIndex: 'used', align: 'center' }, + { + title: '已用百分比', + dataIndex: 'usage', + align: 'center', + render: (usage) => ( + 80 ? 'danger' : 'secondary'}> + {toNumber(usage)}% {renderUsageProgress(usage)} + + ), + }, + ]; + + return ( +
    + +
    + CPU} loading={loading}> + + {serverInfo.cpu?.cpuNum} + + {serverInfo.cpu?.used}% {renderUsageProgress(serverInfo.cpu?.used)} + + + {serverInfo.cpu?.sys}% {renderUsageProgress(serverInfo.cpu?.sys)} + + + {serverInfo.cpu?.free}% {renderUsageProgress(serverInfo.cpu?.free)} + + + + + + + 内存} loading={loading}> + + 总内存 + {serverInfo.mem?.total}G + {serverInfo.jvm?.total}M + + + 80 ? 'danger' : 'secondary'}> + {serverInfo.mem?.used}G + + + + 80 ? 'danger' : 'secondary'}> + {serverInfo.jvm?.used}M + + + + {serverInfo.mem?.free}G + {serverInfo.jvm?.free}M + + {renderUsageProgress(serverInfo.mem?.usage)} + {renderUsageProgress(serverInfo.jvm?.usage)} + + + + + + 服务器信息} loading={loading}> + + {serverInfo.sys?.computerName} + {serverInfo.sys?.osName} + {serverInfo.sys?.computerIp} + {serverInfo.sys?.osArch} + + + + + + Java虚拟机信息} loading={loading}> + + {serverInfo.jvm?.name} + {serverInfo.jvm?.version} + {parseTime(serverInfo.jvm?.startTime)} + {serverInfo.jvm?.runTime} + {serverInfo.jvm?.home} + {serverInfo.sys?.userDir} + {serverInfo.jvm?.inputArgs} + + + + + + 磁盘状态} loading={loading}> +
    + `${record.dirName ?? 'disk'}-${record.sysTypeName ?? ''}-${record.typeName ?? ''}` + } + pagination={false} + size="small" + bordered + columns={diskColumns} + /> + + + + + ); +}; + +export default ServerMonitorPage; diff --git a/src/pages/monitor/cache-list.css b/src/pages/monitor/cache-list.css new file mode 100644 index 0000000..3b91560 --- /dev/null +++ b/src/pages/monitor/cache-list.css @@ -0,0 +1,7 @@ +.cache-list-container .ant-card-body { + padding-top: 8px; +} + +.cache-list-container .ant-table-row { + cursor: pointer; +} diff --git a/src/pages/monitor/cache-monitor.css b/src/pages/monitor/cache-monitor.css new file mode 100644 index 0000000..f47b80d --- /dev/null +++ b/src/pages/monitor/cache-monitor.css @@ -0,0 +1,5 @@ +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} diff --git a/src/pages/monitor/job-monitor.css b/src/pages/monitor/job-monitor.css new file mode 100644 index 0000000..ed4d7e0 --- /dev/null +++ b/src/pages/monitor/job-monitor.css @@ -0,0 +1,11 @@ +.job-monitor-container .ant-form-item { + margin-bottom: 0px; /* Reduce vertical space in search form */ +} + +.job-monitor-container .ant-descriptions-item-content { + display: block; +} + +.job-monitor-container .ant-descriptions-item-label { + width: 120px; /* Adjust label width in details dialog */ +} diff --git a/src/pages/monitor/login-log.css b/src/pages/monitor/login-log.css new file mode 100644 index 0000000..c349486 --- /dev/null +++ b/src/pages/monitor/login-log.css @@ -0,0 +1,3 @@ +.login-log-container .ant-form-item { + margin-bottom: 0px; /* Reduce vertical space in search form */ +} diff --git a/src/pages/monitor/online-user.css b/src/pages/monitor/online-user.css new file mode 100644 index 0000000..3024537 --- /dev/null +++ b/src/pages/monitor/online-user.css @@ -0,0 +1,3 @@ +.online-user-container .ant-form-item { + margin-bottom: 0px; /* Reduce vertical space in search form */ +} diff --git a/src/pages/monitor/operation-log.css b/src/pages/monitor/operation-log.css new file mode 100644 index 0000000..12c89cf --- /dev/null +++ b/src/pages/monitor/operation-log.css @@ -0,0 +1,3 @@ +.operation-log-container .ant-form-item { + margin-bottom: 0px; /* Reduce vertical space in search form */ +} diff --git a/src/pages/monitor/server-monitor.css b/src/pages/monitor/server-monitor.css new file mode 100644 index 0000000..34b7c9d --- /dev/null +++ b/src/pages/monitor/server-monitor.css @@ -0,0 +1,17 @@ +.server-monitor-container .card-box { + margin-bottom: 16px; +} + +.server-monitor-container .ant-descriptions-row > th, +.server-monitor-container .ant-descriptions-row > td { + padding: 8px 16px; +} + +.server-monitor-container .ant-descriptions-item-label { + width: 120px; +} + +.server-monitor-container .ant-progress { + min-width: 100px; + max-width: 100%; +} diff --git a/src/pages/project/DemandManagePage.tsx b/src/pages/project/DemandManagePage.tsx new file mode 100644 index 0000000..7229812 --- /dev/null +++ b/src/pages/project/DemandManagePage.tsx @@ -0,0 +1,1474 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + Badge, + Button, + Card, + Col, + DatePicker, + Dropdown, + Form, + Input, + InputNumber, + message, + Modal, + Pagination, + Popconfirm, + Row, + Select, + Space, + Table, + Typography, +} from 'antd'; +import type { TableColumnsType } from 'antd'; +import { + DownOutlined, + FolderOutlined, + FolderOpenOutlined, + LeftOutlined, + MoreOutlined, + PlusOutlined, + RightOutlined, +} from '@ant-design/icons'; +import dayjs, { Dayjs } from 'dayjs'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { + deleteDemand, + deleteDemandBatch, + deleteProjectVersion, + getDemandDetail, + insertDemand, + insertProjectVersion, + listDemand, + listProjectVersionTree, + updateDemand, + updateProjectVersion, +} from '@/api/project'; +import { getDicts } from '@/api/system/dict'; +import { listUser } from '@/api/system/user'; + +const { Text } = Typography; + +type IdLike = string | number; + +interface DictOption { + value: string; + label: string; +} + +interface UserOption { + value: IdLike; + label: string; +} + +interface DemandQuery { + title?: string; + responsiblePerson?: IdLike; + responsiblePersonName?: string; + demandStatus?: string; + priority?: string; +} + +interface VersionNode { + id: IdLike; + nodeId: string; + type: number; + title: string; + childrenList: VersionNode[]; +} + +interface DemandRow { + key: string; + id: IdLike; + versionId?: IdLike; + versionNumber: string; + title: string; + demandStatus: string; + demandStatusLabel: string; + responsiblePerson?: IdLike; + responsiblePersonName: string; + estimatedWorkHours: number; + createTime: string; + endTime: string; + priority: string; + raw: Record; +} + +interface DemandFormValues { + title: string; + versionId: IdLike; + responsiblePerson: IdLike; + demandStatus: string; + priority: string; + estimatedWorkHours: number; + createTime: Dayjs; + endTime: Dayjs; +} + +interface VersionFormValues { + versionNumber: string; +} + +const DEFAULT_STATUS_OPTIONS: DictOption[] = [ + { value: '0', label: '待排期' }, + { value: '1', label: '已计划' }, + { value: '2', label: '进行中' }, + { value: '3', label: '已完成' }, + { value: '4', label: '已关闭' }, +]; + +const DEFAULT_PRIORITY_OPTIONS: DictOption[] = [ + { value: 'P0', label: 'P0' }, + { value: 'P1', label: 'P1' }, + { value: 'P2', label: 'P2' }, + { value: 'P3', label: 'P3' }, +]; + +const STATUS_COLOR_MAP: Record = { + '0': '#8c8c8c', + '1': '#fa8c16', + '2': '#1677ff', + '3': '#52c41a', + '4': '#bfbfbf', + pending: '#8c8c8c', + planned: '#fa8c16', + in_progress: '#1677ff', + done: '#52c41a', + closed: '#bfbfbf', +}; + +const extractArray = (response: unknown): any[] => { + if (Array.isArray(response)) { + return response; + } + if (response && typeof response === 'object') { + const res = response as Record; + if (Array.isArray(res.data)) { + return res.data as any[]; + } + if (Array.isArray(res.rows)) { + return res.rows as any[]; + } + if (Array.isArray(res.list)) { + return res.list as any[]; + } + } + return []; +}; + +const extractRowsAndTotal = (response: unknown): { rows: any[]; total: number } => { + if (response && typeof response === 'object') { + const res = response as Record; + if (Array.isArray(res.rows)) { + return { rows: res.rows as any[], total: Number(res.total ?? (res.rows as any[]).length) }; + } + if (res.data && typeof res.data === 'object') { + const data = res.data as Record; + if (Array.isArray(data.rows)) { + return { rows: data.rows as any[], total: Number(data.total ?? (data.rows as any[]).length) }; + } + if (Array.isArray(data.list)) { + return { rows: data.list as any[], total: Number(data.total ?? (data.list as any[]).length) }; + } + } + if (Array.isArray(res.list)) { + return { rows: res.list as any[], total: Number(res.total ?? (res.list as any[]).length) }; + } + } + if (Array.isArray(response)) { + return { rows: response as any[], total: (response as any[]).length }; + } + return { rows: [], total: 0 }; +}; + +const toDayjs = (value: unknown) => { + if (value === null || value === undefined || value === '') { + return null; + } + const parsed = dayjs(value as dayjs.ConfigType); + return parsed.isValid() ? parsed : null; +}; + +const formatDate = (value: unknown) => { + const parsed = toDayjs(value); + if (!parsed) { + return '-'; + } + return parsed.format('YYYY-MM-DD'); +}; + +const formatApiDate = (value: unknown, isEnd: boolean) => { + const parsed = toDayjs(value); + if (!parsed) { + return undefined; + } + return parsed.format(isEnd ? 'YYYY-MM-DD 23:59:59' : 'YYYY-MM-DD 00:00:00'); +}; + +const normalizeStatusValue = (value: unknown) => { + const raw = String(value ?? '').trim(); + const lower = raw.toLowerCase(); + if (lower === 'pending' || raw === '0' || raw.includes('待')) return '0'; + if (lower === 'planned' || raw === '1' || raw.includes('计划')) return '1'; + if (lower === 'in_progress' || raw === '2' || raw.includes('进行')) return '2'; + if (lower === 'done' || raw === '3' || raw.includes('完成')) return '3'; + if (lower === 'closed' || raw === '4' || raw.includes('关闭')) return '4'; + return raw; +}; + +const normalizeVersionNode = (node: any): VersionNode | null => { + const id = node?.id ?? node?.versionId ?? node?.value; + if (id === undefined || id === null) { + return null; + } + const type = Number(node?.type ?? 0); + const childrenRaw = Array.isArray(node?.childrenList) + ? node.childrenList + : Array.isArray(node?.children) + ? node.children + : []; + const childrenList = childrenRaw + .map((item: any) => normalizeVersionNode(item)) + .filter((item: VersionNode | null): item is VersionNode => Boolean(item)); + + return { + id, + nodeId: String(node?.nodeId ?? `${id}_${Number.isNaN(type) ? 0 : type}`), + type: Number.isNaN(type) ? 0 : type, + title: String(node?.title ?? node?.versionNumber ?? node?.versionName ?? node?.name ?? id), + childrenList, + }; +}; + +const flattenVersionNodes = (nodes: VersionNode[]) => { + const result: VersionNode[] = []; + const walk = (list: VersionNode[]) => { + list.forEach((item) => { + result.push(item); + if (item.childrenList.length) { + walk(item.childrenList); + } + }); + }; + walk(nodes); + return result; +}; + +const buildEndDate = (start: Dayjs, workHours: number) => { + if (workHours > 1) { + return dayjs(start).add(Math.ceil(workHours - 1), 'day').endOf('day'); + } + return dayjs(start).endOf('day'); +}; + +const DemandManagePage: React.FC = () => { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const [queryForm] = Form.useForm(); + const [demandForm] = Form.useForm(); + const [versionForm] = Form.useForm(); + + const projectId = searchParams.get('id') ?? ''; + const projectName = searchParams.get('projectName') ?? '未命名项目'; + const startDate = searchParams.get('startDate'); + const endDate = searchParams.get('endDate'); + + const projectStartMs = startDate ? Number(startDate) : undefined; + const projectEndMs = endDate ? Number(endDate) : undefined; + const projectStartText = startDate ? dayjs(Number(startDate)).format('YYYY-MM-DD') : '-'; + const projectEndText = endDate ? dayjs(Number(endDate)).format('YYYY-MM-DD') : '-'; + + const [loading, setLoading] = useState(false); + const [submitLoading, setSubmitLoading] = useState(false); + + const [versionTree, setVersionTree] = useState([]); + const [versionList, setVersionList] = useState([]); + const [selectedVersion, setSelectedVersion] = useState<{ id: IdLike | ''; nodeId: string; type: number; title: string }>( + { + id: projectId, + nodeId: 'all', + type: 2, + title: '全部版本', + }, + ); + + const [statusOptions, setStatusOptions] = useState(DEFAULT_STATUS_OPTIONS); + const [priorityOptions, setPriorityOptions] = useState(DEFAULT_PRIORITY_OPTIONS); + const [userOptions, setUserOptions] = useState([]); + + const [demands, setDemands] = useState([]); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [total, setTotal] = useState(0); + const [pageNum, setPageNum] = useState(1); + const pageSize = 10; + + const [query, setQuery] = useState({}); + + const [demandModalOpen, setDemandModalOpen] = useState(false); + const [versionModalOpen, setVersionModalOpen] = useState(false); + const [editingDemand, setEditingDemand] = useState(null); + const [editingVersion, setEditingVersion] = useState(null); + const [versionSidebarCollapsed, setVersionSidebarCollapsed] = useState(false); + const [versionOpen, setVersionOpen] = useState(true); + const [expandedVersionNodeIds, setExpandedVersionNodeIds] = useState([]); + const [hoverVersionNodeId, setHoverVersionNodeId] = useState(''); + const tableContainerRef = useRef(null); + const [tableScrollY, setTableScrollY] = useState(320); + + const statusLabelMap = useMemo(() => { + const map = new Map(); + statusOptions.forEach((item) => { + map.set(String(item.value), item.label); + }); + return map; + }, [statusOptions]); + + const hasUserOptions = userOptions.length > 0; + + const allVersionEntry = useMemo( + () => ({ + id: projectId || 'all', + nodeId: 'all', + type: 2, + title: '全部版本', + childrenList: [], + }), + [projectId], + ); + + const versionRows = useMemo(() => versionTree.filter((item) => item.type === 0), [versionTree]); + const versionChildNodeIds = useMemo(() => { + const idSet = new Set(); + versionRows.forEach((item) => { + item.childrenList.forEach((child) => idSet.add(child.nodeId)); + }); + return idSet; + }, [versionRows]); + const fileRows = useMemo( + () => + flattenVersionNodes(versionTree).filter((item) => { + return item.type !== 0 && !versionChildNodeIds.has(item.nodeId); + }), + [versionChildNodeIds, versionTree], + ); + + const toDemandRow = useCallback( + (item: any, index: number): DemandRow => { + const id = item?.id ?? item?.demandId ?? `row-${index}`; + const demandStatus = normalizeStatusValue(item?.demandStatus ?? item?.status ?? item?.statusKey); + const demandStatusLabel = statusLabelMap.get(demandStatus) ?? String(item?.statusName ?? item?.demandStatus ?? ''); + return { + key: String(id), + id, + versionId: item?.versionId ?? item?.projectVersionId, + versionNumber: String(item?.versionNumber ?? item?.versionName ?? item?.versionNo ?? '-'), + title: String(item?.title ?? item?.demandTitle ?? '-'), + demandStatus, + demandStatusLabel: demandStatusLabel || demandStatus, + responsiblePerson: item?.responsiblePerson ?? item?.ownerId, + responsiblePersonName: String(item?.responsiblePersonName ?? item?.ownerName ?? item?.owner ?? '-'), + estimatedWorkHours: Number(item?.estimatedWorkHours ?? item?.budgetDays ?? item?.workDate ?? 0), + createTime: formatDate(item?.createTime ?? item?.startDate), + endTime: formatDate(item?.endTime ?? item?.finishTime ?? item?.endDate), + priority: String(item?.priority ?? item?.priorityLevel ?? 'P2'), + raw: item as Record, + }; + }, + [statusLabelMap], + ); + + const loadDictOptions = useCallback(async () => { + try { + const [statusRes, priorityRes] = await Promise.all([getDicts('demand_status'), getDicts('demand_priority')]); + const statusData = extractArray(statusRes); + const priorityData = extractArray(priorityRes); + + const parsedStatus = statusData + .map((item) => ({ value: String(item?.dictValue ?? ''), label: String(item?.dictLabel ?? '') })) + .filter((item) => item.value !== '' && item.label !== ''); + + const parsedPriority = priorityData + .map((item) => ({ value: String(item?.dictValue ?? ''), label: String(item?.dictLabel ?? '') })) + .filter((item) => item.value !== '' && item.label !== ''); + + if (parsedStatus.length) { + setStatusOptions(parsedStatus); + } + if (parsedPriority.length) { + setPriorityOptions(parsedPriority); + } + } catch { + // keep fallback options + } + }, []); + + const loadUserOptions = useCallback(async () => { + try { + const response = await listUser({ pageNum: 1, pageSize: 500 }); + const { rows } = extractRowsAndTotal(response); + const userMap = new Map(); + rows.forEach((item) => { + const id = item?.userId; + if (id === undefined || id === null) { + return; + } + const label = String(item?.nickName ?? item?.userName ?? item?.userNickName ?? id); + const key = String(id); + if (!userMap.has(key)) { + userMap.set(key, { value: id, label }); + } + }); + setUserOptions(Array.from(userMap.values())); + } catch { + setUserOptions([]); + } + }, []); + + const fetchVersions = useCallback(async () => { + if (!projectId) { + setVersionTree([]); + setVersionList([]); + setExpandedVersionNodeIds([]); + return; + } + + const fallbackFromDemand = async () => { + const response = await listDemand({ pageNum: 1, pageSize: 200, projectId }); + const { rows } = extractRowsAndTotal(response); + const versionMap = new Map(); + rows.forEach((item) => { + const id = item?.versionId ?? item?.projectVersionId; + if (id === undefined || id === null) { + return; + } + const nodeId = String(id); + if (!versionMap.has(nodeId)) { + versionMap.set(nodeId, { + id, + nodeId, + type: 0, + title: String(item?.versionNumber ?? item?.versionName ?? item?.versionNo ?? id), + childrenList: [], + }); + } + }); + return Array.from(versionMap.values()).map((item) => ({ ...item, childrenList: [] })); + }; + + try { + const response = await listProjectVersionTree(projectId); + const nodes = extractArray(response) + .map((item) => normalizeVersionNode(item)) + .filter((item: VersionNode | null): item is VersionNode => Boolean(item)); + + let tree = nodes.filter((item) => item.type === 0); + if (tree.length === 0) { + tree = flattenVersionNodes(nodes).filter((item) => item.type === 0).map((item) => ({ + ...item, + childrenList: [], + })); + } + + if (tree.length === 0) { + tree = await fallbackFromDemand(); + } + + const versions = flattenVersionNodes(tree).filter((item) => item.type === 0); + setVersionTree(tree); + setVersionList(versions); + setExpandedVersionNodeIds((prev) => { + if (prev.length > 0) { + return prev; + } + const first = tree[0]?.nodeId; + return first ? [first] : []; + }); + } catch { + try { + const tree = await fallbackFromDemand(); + setVersionTree(tree); + setVersionList(tree); + setExpandedVersionNodeIds((prev) => { + if (prev.length > 0) { + return prev; + } + const first = tree[0]?.nodeId; + return first ? [first] : []; + }); + } catch { + setVersionTree([]); + setVersionList([]); + setExpandedVersionNodeIds([]); + } + } + }, [projectId]); + + const fetchDemands = useCallback(async () => { + setLoading(true); + try { + const params: Record = { + pageNum, + pageSize, + }; + + if (selectedVersion.type === 0) { + params.versionId = selectedVersion.id; + } else if (selectedVersion.type === 1) { + params.id = selectedVersion.id; + } else if (projectId) { + params.projectId = projectId; + } + + if (query.title) { + params.title = query.title; + } + if (query.responsiblePerson !== undefined && query.responsiblePerson !== '') { + params.responsiblePerson = query.responsiblePerson; + } + if (query.responsiblePersonName) { + params.responsiblePersonName = query.responsiblePersonName; + } + if (query.demandStatus) { + params.demandStatus = query.demandStatus; + } + if (query.priority) { + params.priority = query.priority; + } + + const response = await listDemand(params); + const { rows, total: totalCount } = extractRowsAndTotal(response); + setDemands(rows.map((item, index) => toDemandRow(item, index))); + setTotal(totalCount); + } catch { + message.error('获取需求列表失败'); + } finally { + setLoading(false); + } + }, [pageNum, pageSize, projectId, query, selectedVersion, toDemandRow]); + + useEffect(() => { + loadDictOptions(); + loadUserOptions(); + }, [loadDictOptions, loadUserOptions]); + + useEffect(() => { + fetchVersions(); + }, [fetchVersions]); + + useEffect(() => { + const allNodes = flattenVersionNodes(versionTree); + const validSelection = + selectedVersion.nodeId === 'all' || + allNodes.some((item) => String(item.nodeId) === String(selectedVersion.nodeId)); + if (!validSelection) { + setSelectedVersion({ id: projectId, nodeId: 'all', type: 2, title: '全部版本' }); + } + }, [projectId, selectedVersion.nodeId, versionTree]); + + useEffect(() => { + fetchDemands(); + }, [fetchDemands]); + + const updateTableScrollY = useCallback(() => { + if (!tableContainerRef.current) { + return; + } + const top = tableContainerRef.current.getBoundingClientRect().top; + const available = window.innerHeight - top - 170; + setTableScrollY(Math.max(260, Math.floor(available))); + }, []); + + useEffect(() => { + updateTableScrollY(); + window.addEventListener('resize', updateTableScrollY); + return () => { + window.removeEventListener('resize', updateTableScrollY); + }; + }, [updateTableScrollY]); + + useEffect(() => { + const timer = window.setTimeout(() => { + updateTableScrollY(); + }, 0); + return () => window.clearTimeout(timer); + }, [demands.length, pageNum, versionOpen, query, updateTableScrollY]); + + const handleSearch = () => { + const values = queryForm.getFieldsValue(); + if (hasUserOptions) { + const selectedUser = userOptions.find((item) => String(item.value) === String(values.responsiblePerson)); + setQuery({ + title: values.title?.trim() || undefined, + responsiblePerson: values.responsiblePerson || undefined, + responsiblePersonName: selectedUser?.label, + demandStatus: values.demandStatus || undefined, + priority: values.priority || undefined, + }); + } else { + setQuery({ + title: values.title?.trim() || undefined, + responsiblePersonName: values.responsiblePerson?.trim() || undefined, + demandStatus: values.demandStatus || undefined, + priority: values.priority || undefined, + }); + } + setPageNum(1); + }; + + const handleReset = () => { + queryForm.resetFields(); + setQuery({}); + setPageNum(1); + }; + + const openCreateDemand = () => { + setEditingDemand(null); + const defaultVersionId = selectedVersion.type === 0 ? selectedVersion.id : versionList[0]?.id; + demandForm.setFieldsValue({ + title: '', + versionId: defaultVersionId, + responsiblePerson: undefined, + demandStatus: statusOptions[0]?.value, + priority: priorityOptions[0]?.value, + estimatedWorkHours: 1, + createTime: dayjs().startOf('day'), + endTime: dayjs().endOf('day'), + }); + setDemandModalOpen(true); + }; + + const openCreateDemandForVersion = (version: VersionNode) => { + setSelectedVersion({ + id: version.id, + nodeId: version.nodeId, + type: version.type, + title: version.title, + }); + setEditingDemand(null); + demandForm.setFieldsValue({ + title: '', + versionId: version.id, + responsiblePerson: undefined, + demandStatus: statusOptions[0]?.value, + priority: priorityOptions[0]?.value, + estimatedWorkHours: 1, + createTime: dayjs().startOf('day'), + endTime: dayjs().endOf('day'), + }); + setDemandModalOpen(true); + }; + + const openEditDemand = async (record: DemandRow) => { + setEditingDemand(record); + + let source: Record = record.raw; + try { + const detailRes = await getDemandDetail(record.id); + source = ((detailRes as Record).data ?? detailRes) as Record; + } catch { + // Use table row data as fallback. + } + + const userIdFromName = userOptions.find( + (item) => item.label === String(source.responsiblePersonName ?? record.responsiblePersonName), + )?.value; + + const createTime = + toDayjs(source.createTime ?? source.startDate ?? record.createTime) ?? dayjs().startOf('day'); + const endTime = + toDayjs(source.endTime ?? source.finishTime ?? record.endTime) ?? dayjs(createTime).endOf('day'); + + demandForm.setFieldsValue({ + title: String(source.title ?? record.title), + versionId: (source.versionId as IdLike) ?? record.versionId, + responsiblePerson: + (source.responsiblePerson as IdLike) ?? + record.responsiblePerson ?? + userIdFromName ?? + (hasUserOptions ? undefined : record.responsiblePersonName), + demandStatus: normalizeStatusValue(source.demandStatus ?? source.status ?? record.demandStatus), + priority: String(source.priority ?? record.priority), + estimatedWorkHours: Number(source.estimatedWorkHours ?? record.estimatedWorkHours ?? 1), + createTime, + endTime, + }); + setDemandModalOpen(true); + }; + + const handleDemandFormChange = (_changed: Partial, all: DemandFormValues) => { + if (!all.createTime || !all.estimatedWorkHours) { + return; + } + demandForm.setFieldValue('endTime', buildEndDate(all.createTime, Number(all.estimatedWorkHours))); + }; + + const submitDemand = async () => { + try { + const values = await demandForm.validateFields(); + setSubmitLoading(true); + + const selectedUser = userOptions.find((item) => String(item.value) === String(values.responsiblePerson)); + const responsiblePersonName = hasUserOptions + ? selectedUser?.label ?? (editingDemand?.responsiblePersonName !== '-' ? editingDemand?.responsiblePersonName : '') + : String(values.responsiblePerson ?? '').trim(); + + const payload = { + id: editingDemand?.id, + demandId: editingDemand?.id, + projectId, + versionId: values.versionId, + title: values.title, + demandTitle: values.title, + demandStatus: values.demandStatus, + status: values.demandStatus, + priority: values.priority, + responsiblePerson: values.responsiblePerson, + responsiblePersonName, + estimatedWorkHours: Number(values.estimatedWorkHours), + budgetDays: Number(values.estimatedWorkHours), + createTime: values.createTime.format('YYYY-MM-DD 00:00:00'), + endTime: values.endTime.format('YYYY-MM-DD 23:59:59'), + }; + + if (editingDemand) { + await updateDemand(payload); + message.success('需求修改成功'); + } else { + await insertDemand(payload); + message.success('需求新增成功'); + } + + setDemandModalOpen(false); + setSelectedRowKeys([]); + fetchDemands(); + fetchVersions(); + } catch { + // Validate/API error is handled by antd/request interceptor. + } finally { + setSubmitLoading(false); + } + }; + + const buildQuickUpdatePayload = (record: DemandRow, patch: Partial>) => { + const responsiblePerson = (patch.responsiblePerson as IdLike | undefined) ?? record.responsiblePerson; + const responsiblePersonName = + (patch.responsiblePersonName as string | undefined) ?? + userOptions.find((item) => String(item.value) === String(responsiblePerson))?.label ?? + record.responsiblePersonName; + + return { + id: record.id, + demandId: record.id, + projectId, + versionId: record.versionId, + title: record.title, + demandTitle: record.title, + demandStatus: (patch.demandStatus as string | undefined) ?? record.demandStatus, + status: (patch.demandStatus as string | undefined) ?? record.demandStatus, + priority: (patch.priority as string | undefined) ?? record.priority, + responsiblePerson, + responsiblePersonName, + estimatedWorkHours: Number(record.estimatedWorkHours), + budgetDays: Number(record.estimatedWorkHours), + createTime: formatApiDate(record.createTime, false), + endTime: formatApiDate(record.endTime, true), + }; + }; + + const handleQuickUpdate = async (record: DemandRow, patch: Partial>) => { + try { + await updateDemand(buildQuickUpdatePayload(record, patch)); + setDemands((prev) => + prev.map((item) => (item.id === record.id ? { ...item, ...patch } as DemandRow : item)), + ); + } catch { + message.error('更新失败'); + fetchDemands(); + } + }; + + const handleDeleteOne = async (record: DemandRow) => { + try { + await deleteDemand(record.id); + message.success('删除成功'); + setSelectedRowKeys([]); + fetchDemands(); + fetchVersions(); + } catch { + message.error('删除失败'); + } + }; + + const handleDeleteBatch = async () => { + if (selectedRowKeys.length === 0) { + message.warning('请选择要删除的数据'); + return; + } + try { + await deleteDemandBatch(selectedRowKeys.join(',')); + message.success('删除成功'); + setSelectedRowKeys([]); + fetchDemands(); + fetchVersions(); + } catch { + message.error('删除失败'); + } + }; + + const openCreateVersion = () => { + setEditingVersion(null); + versionForm.setFieldsValue({ versionNumber: '' }); + setVersionModalOpen(true); + }; + + const openEditVersion = (item: VersionNode) => { + setEditingVersion(item); + versionForm.setFieldsValue({ versionNumber: item.title }); + setVersionModalOpen(true); + }; + + const submitVersion = async () => { + try { + const values = await versionForm.validateFields(); + setSubmitLoading(true); + const payload = { + projectId, + versionNumber: values.versionNumber.trim(), + }; + + if (editingVersion) { + await updateProjectVersion({ ...payload, id: editingVersion.id }); + message.success('版本修改成功'); + } else { + await insertProjectVersion(payload); + message.success('版本新增成功'); + } + + setVersionModalOpen(false); + fetchVersions(); + fetchDemands(); + } catch { + // Validate/API error handled by antd/request interceptor. + } finally { + setSubmitLoading(false); + } + }; + + const handleDeleteVersion = async (item: VersionNode) => { + try { + await deleteProjectVersion(item.id); + message.success('版本删除成功'); + if (selectedVersion.nodeId === item.nodeId) { + setSelectedVersion({ id: projectId, nodeId: 'all', type: 2, title: '全部版本' }); + } + fetchVersions(); + fetchDemands(); + } catch { + message.error('版本删除失败'); + } + }; + + const handleVersionMenuAction = (action: string, item: VersionNode) => { + if (action === 'add') { + openCreateDemandForVersion(item); + return; + } + if (action === 'edit') { + openEditVersion(item); + return; + } + if (action === 'delete') { + Modal.confirm({ + title: '此操作将永久删除该版本号,是否继续?', + okText: '确定', + cancelText: '取消', + onOk: () => handleDeleteVersion(item), + }); + } + }; + + const statusSelectOptions = statusOptions; + + const columns: TableColumnsType = [ + { + title: '序号', + width: 70, + render: (_value, _record, index) => (pageNum - 1) * pageSize + index + 1, + }, + { title: '版本号', dataIndex: 'versionNumber', key: 'versionNumber', width: 110 }, + { title: '标题', dataIndex: 'title', key: 'title', ellipsis: true }, + { + title: '需求状态', + dataIndex: 'demandStatus', + key: 'demandStatus', + width: 170, + render: (_value, record) => ( + + + { + if (value !== record.priority) { + handleQuickUpdate(record, { priority: value }); + } + }} + /> + ), + }, + { + title: '操作', + key: 'operation', + width: 150, + render: (_value, record) => ( + + + handleDeleteOne(record)} + > + + + + ), + }, + ]; + + const disableOutOfRangeDate = (current: Dayjs) => { + if (!current) { + return false; + } + const currentValue = current.valueOf(); + if (projectStartMs !== undefined && currentValue < projectStartMs) { + return true; + } + if (projectEndMs !== undefined && currentValue > projectEndMs) { + return true; + } + return false; + }; + + return ( +
    + +
    + {versionSidebarCollapsed ? ( +
    +
    + ) : ( + + 版本列表 + + } onClick={() => navigate('/project/list')}> + 返回项目列表 + + } + > + + 项目名称:{projectName} + 项目ID:{projectId || '-'} + 项目周期:{projectStartText} ~ {projectEndText} + + +
    + + + + + {hasUserOptions ? ( + + )} + + + + + + + + + + + + + + + + + +
    + + rowKey="id" + columns={columns} + dataSource={demands} + loading={loading} + pagination={false} + scroll={{ x: 'max-content', y: tableScrollY }} + rowSelection={{ + selectedRowKeys, + onChange: (keys) => setSelectedRowKeys(keys), + }} + /> +
    + +
    + 共 {total} 条 + setPageNum(page)} + /> +
    +
    + + + + setDemandModalOpen(false)} + confirmLoading={submitLoading} + width={700} + > + + form={demandForm} + layout="vertical" + onValuesChange={handleDemandFormChange} + > + + + + + + + + + + + + + + + + + + ) : ( + + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setVersionModalOpen(false)} + confirmLoading={submitLoading} + > + form={versionForm} layout="vertical"> + + + + + + + ); +}; + +export default DemandManagePage; diff --git a/src/pages/project/ProjectDetailPage.tsx b/src/pages/project/ProjectDetailPage.tsx new file mode 100644 index 0000000..5db3b66 --- /dev/null +++ b/src/pages/project/ProjectDetailPage.tsx @@ -0,0 +1,135 @@ +import React, { useState, useEffect } from 'react'; +import { Form, Input, Button, message, Card, Row, Col, DatePicker, InputNumber } from 'antd'; +import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; +import { getProjectDetail, addProject, updateProject, getProjectCode } from '../../api/project'; +import dayjs from 'dayjs'; +import { UserOutlined } from '@ant-design/icons'; +import PageBackButton from '@/components/PageBackButton'; + +const { TextArea } = Input; + +const ProjectDetailPage: React.FC = () => { + const [form] = Form.useForm(); + const { id: pathId } = useParams<{ id: string }>(); + const [searchParams] = useSearchParams(); + const id = pathId ?? searchParams.get('id') ?? undefined; + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); + const isEdit = !!id; + + useEffect(() => { + const fetchProjectData = async () => { + if (isEdit) { + setLoading(true); + try { + const response = await getProjectDetail(id as string); + const projectData = ((response as Record).data ?? response) as Record; + const startValue = projectData.startDate as dayjs.ConfigType | undefined; + const endValue = projectData.endDate as dayjs.ConfigType | undefined; + form.setFieldsValue({ + ...projectData, + startDate: startValue ? dayjs(startValue) : null, + endDate: endValue ? dayjs(endValue) : null, + }); + } catch (error) { + message.error('获取项目详情失败'); + } finally { + setLoading(false); + } + } else { + // Fetch project code for new project + try { + const response = await getProjectCode(); + form.setFieldsValue({ projectCode: ((response as Record).data ?? response) as string }); + } catch(error) { + message.error('获取项目编号失败'); + } + } + }; + fetchProjectData(); + }, [id, isEdit, form]); + + const onFinish = async (values: any) => { + setLoading(true); + try { + const payload = { + ...values, + startDate: values.startDate ? values.startDate.format('YYYY-MM-DD HH:mm:ss') : null, + endDate: values.endDate ? values.endDate.format('YYYY-MM-DD HH:mm:ss') : null, + }; + if (isEdit) { + await updateProject({ ...payload, projectId: id }); + message.success('修改成功'); + } else { + await addProject(payload); + message.success('新增成功'); + } + navigate('/project/list'); + } catch (error) { + message.error(isEdit ? '修改失败' : '新增失败'); + } finally { + setLoading(false); + } + }; + + return ( +
    +
    + +
    + +
    + +
    + + + + + + + + + + + + + + } /> + + + + + + + + + + + + + + + + + + + + + +