From 6b55c1861040aa531f65ffa333923187e4e9747e Mon Sep 17 00:00:00 2001 From: Cheng Zhou Date: Thu, 16 Jul 2026 14:58:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor(frontend):=20=E6=8C=89=E9=9C=80?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=A1=B5=E9=9D=A2=E5=B9=B6=E6=B8=85=E7=90=86?= =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将业务页面路由统一改为动态导入,拆分首屏入口与各功能模块构建产物。 - 将网页端和桌面端布局改为异步组件,避免两套平台布局同时进入初始包。 - 新增 Element Plus 按需安装入口,并通过全局配置组件统一注入中文语言包。 - 提取 API 运行时钩子,在应用启动时注入项目清理、令牌续期和认证失效退出能力。 - 将权限监控面板及地图资源改为延迟加载,补充地图加载状态、失败提示和切换竞态保护。 - 删除已被现有工作流替代的项目成员、接口权限、中心绑定、培训表单及旧项目首页等页面。 - 清理废弃的快捷操作、项目选择、用户选择、FAQ 表单、风险占位组件和旧地图辅助模块。 - 移除未使用的 API 方法、类型、字典、状态机、展示工具、样式和项目详情编辑逻辑。 - 开启 TypeScript 未使用变量与参数检查,并同步收紧相关测试和组件暴露类型。 - 移除未使用的 updater、date-fns 和 Sass 前端依赖,更新锁文件并删除旧 CSS 清洗插件。 - 更新路由、Axios、ETMF、通知、权限监控和桌面布局测试以覆盖重构后的边界。 --- frontend/package-lock.json | 898 ------------------ frontend/package.json | 3 - frontend/src/App.vue | 11 +- frontend/src/api/authClient.ts | 6 - frontend/src/api/axios.test.ts | 18 - frontend/src/api/axios.ts | 27 +- frontend/src/api/collaboration.test.ts | 9 - frontend/src/api/collaboration.ts | 3 - frontend/src/api/dashboard.ts | 40 - frontend/src/api/desktopNotifications.ts | 6 - frontend/src/api/documents.ts | 1 - frontend/src/api/etmf.test.ts | 6 +- frontend/src/api/etmf.ts | 7 +- frontend/src/api/notifications.test.ts | 5 - frontend/src/api/notifications.ts | 8 +- frontend/src/api/projectPermissions.ts | 35 - frontend/src/api/runtimeHooks.ts | 28 + frontend/src/api/subjectHistories.ts | 3 - frontend/src/audit/index.ts | 7 - frontend/src/components/DesktopLayout.vue | 1 - frontend/src/components/FaqCategoryForm.vue | 310 ------ .../src/components/FaqCategoryManager.vue | 2 +- .../src/components/Layout.desktop.test.ts | 2 + frontend/src/components/Layout.vue | 6 +- .../components/PermissionAccessLogs.test.ts | 8 +- .../src/components/PermissionAccessLogs.vue | 27 - .../components/PermissionIpLocations.test.ts | 4 +- .../src/components/PermissionIpLocations.vue | 76 +- .../src/components/PermissionMonitoring.vue | 45 +- frontend/src/components/QuickActions.vue | 145 --- frontend/src/components/SecurityCenter.vue | 2 - frontend/src/components/StudySelector.vue | 79 -- frontend/src/components/WebLayout.vue | 2 +- frontend/src/components/chinaProvinceMap.ts | 14 - .../src/components/selectors/UserSelect.vue | 49 - frontend/src/dictionaries/utils.ts | 4 - frontend/src/locales/index.ts | 14 - frontend/src/main.ts | 15 +- frontend/src/plugins/elementPlus.ts | 132 +++ frontend/src/router.test.ts | 4 +- frontend/src/router/index.ts | 106 +-- frontend/src/state-machine/index.ts | 15 - frontend/src/types/api.ts | 5 - frontend/src/types/documents.ts | 33 - frontend/src/types/etmf.ts | 18 +- frontend/src/types/visits.ts | 9 - frontend/src/utils/display/index.ts | 5 - frontend/src/utils/permission.ts | 53 -- frontend/src/views/DesktopProjectEntry.vue | 3 - frontend/src/views/Register.vue | 2 +- frontend/src/views/StudyHome.vue | 371 -------- .../src/views/admin/ApiPermissions.test.ts | 71 -- frontend/src/views/admin/ApiPermissions.vue | 229 ----- .../views/admin/PermissionManagement.test.ts | 3 +- .../src/views/admin/PermissionManagement.vue | 19 +- frontend/src/views/admin/ProjectDetail.vue | 203 ---- frontend/src/views/admin/ProjectMembers.vue | 419 -------- frontend/src/views/admin/SiteCraBinding.vue | 115 --- frontend/src/views/documents/DocumentList.vue | 5 - frontend/src/views/ia/EtmfPlaceholder.vue | 6 - frontend/src/views/ia/RiskIssues.vue | 13 - frontend/src/views/startup/TrainingForm.vue | 192 ---- .../src/views/subjects/VisitEditorDrawer.vue | 6 +- frontend/tsconfig.json | 2 + frontend/vite.config.ts | 19 +- 65 files changed, 345 insertions(+), 3639 deletions(-) delete mode 100644 frontend/src/api/dashboard.ts create mode 100644 frontend/src/api/runtimeHooks.ts delete mode 100644 frontend/src/components/FaqCategoryForm.vue delete mode 100644 frontend/src/components/QuickActions.vue delete mode 100644 frontend/src/components/StudySelector.vue delete mode 100644 frontend/src/components/chinaProvinceMap.ts delete mode 100644 frontend/src/components/selectors/UserSelect.vue create mode 100644 frontend/src/plugins/elementPlus.ts delete mode 100644 frontend/src/types/visits.ts delete mode 100644 frontend/src/views/StudyHome.vue delete mode 100644 frontend/src/views/admin/ApiPermissions.test.ts delete mode 100644 frontend/src/views/admin/ApiPermissions.vue delete mode 100644 frontend/src/views/admin/ProjectMembers.vue delete mode 100644 frontend/src/views/admin/SiteCraBinding.vue delete mode 100644 frontend/src/views/ia/RiskIssues.vue delete mode 100644 frontend/src/views/startup/TrainingForm.vue diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6bfc715d..5dea880c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,9 +13,7 @@ "@tauri-apps/plugin-fs": "^2.4.0", "@tauri-apps/plugin-notification": "^2.3.0", "@tauri-apps/plugin-opener": "^2.5.0", - "@tauri-apps/plugin-updater": "^2.9.0", "axios": "^1.6.8", - "date-fns": "^3.6.0", "echarts": "^6.0.0", "element-plus": "^2.4.4", "pdfjs-dist": "6.1.200", @@ -30,7 +28,6 @@ "@vitejs/plugin-vue": "^6.0.3", "@vue/test-utils": "^2.4.6", "jsdom": "^26.1.0", - "sass-embedded": "^1.77.0", "typescript": "^5.3.3", "vite": "^7.3.1", "vitest": "^3.2.4", @@ -92,13 +89,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bufbuild/protobuf": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.12.0.tgz", - "integrity": "sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA==", - "dev": true, - "license": "(Apache-2.0 AND BSD-3-Clause)" - }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -983,334 +973,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@parcel/watcher": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", - "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.3", - "is-glob": "^4.0.3", - "node-addon-api": "^7.0.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.6", - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6", - "@parcel/watcher-freebsd-x64": "2.5.6", - "@parcel/watcher-linux-arm-glibc": "2.5.6", - "@parcel/watcher-linux-arm-musl": "2.5.6", - "@parcel/watcher-linux-arm64-glibc": "2.5.6", - "@parcel/watcher-linux-arm64-musl": "2.5.6", - "@parcel/watcher-linux-x64-glibc": "2.5.6", - "@parcel/watcher-linux-x64-musl": "2.5.6", - "@parcel/watcher-win32-arm64": "2.5.6", - "@parcel/watcher-win32-ia32": "2.5.6", - "@parcel/watcher-win32-x64": "2.5.6" - } - }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", - "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", - "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", - "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", - "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", - "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", - "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", - "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", - "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", - "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", - "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", - "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1908,15 +1570,6 @@ "@tauri-apps/api": "^2.11.0" } }, - "node_modules/@tauri-apps/plugin-updater": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz", - "integrity": "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==", - "license": "MIT OR Apache-2.0", - "dependencies": { - "@tauri-apps/api": "^2.10.1" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2422,23 +2075,6 @@ "node": ">= 16" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2459,13 +2095,6 @@ "dev": true, "license": "MIT" }, - "node_modules/colorjs.io": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", - "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", - "dev": true, - "license": "MIT" - }, "node_modules/combined-stream": { "version": "1.0.8", "license": "MIT", @@ -2544,16 +2173,6 @@ "node": ">=18" } }, - "node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/dayjs": { "version": "1.11.19", "license": "MIT" @@ -2600,17 +2219,6 @@ "node": ">=0.4.0" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "license": "MIT", @@ -2959,16 +2567,6 @@ "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", "license": "MIT", @@ -3056,13 +2654,6 @@ "node": ">=0.10.0" } }, - "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "dev": true, - "license": "MIT" - }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", @@ -3070,17 +2661,6 @@ "dev": true, "license": "ISC" }, - "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", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3091,20 +2671,6 @@ "node": ">=8" } }, - "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", - "optional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -3336,14 +2902,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", @@ -3551,21 +3109,6 @@ "node": ">=6" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/rollup": { "version": "4.53.5", "dev": true, @@ -3627,16 +3170,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3644,384 +3177,6 @@ "dev": true, "license": "MIT" }, - "node_modules/sass": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", - "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^5.1.5", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" - } - }, - "node_modules/sass-embedded": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.99.0.tgz", - "integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bufbuild/protobuf": "^2.5.0", - "colorjs.io": "^0.5.0", - "immutable": "^5.1.5", - "rxjs": "^7.4.0", - "supports-color": "^8.1.1", - "sync-child-process": "^1.0.2", - "varint": "^6.0.0" - }, - "bin": { - "sass": "dist/bin/sass.js" - }, - "engines": { - "node": ">=16.0.0" - }, - "optionalDependencies": { - "sass-embedded-all-unknown": "1.99.0", - "sass-embedded-android-arm": "1.99.0", - "sass-embedded-android-arm64": "1.99.0", - "sass-embedded-android-riscv64": "1.99.0", - "sass-embedded-android-x64": "1.99.0", - "sass-embedded-darwin-arm64": "1.99.0", - "sass-embedded-darwin-x64": "1.99.0", - "sass-embedded-linux-arm": "1.99.0", - "sass-embedded-linux-arm64": "1.99.0", - "sass-embedded-linux-musl-arm": "1.99.0", - "sass-embedded-linux-musl-arm64": "1.99.0", - "sass-embedded-linux-musl-riscv64": "1.99.0", - "sass-embedded-linux-musl-x64": "1.99.0", - "sass-embedded-linux-riscv64": "1.99.0", - "sass-embedded-linux-x64": "1.99.0", - "sass-embedded-unknown-all": "1.99.0", - "sass-embedded-win32-arm64": "1.99.0", - "sass-embedded-win32-x64": "1.99.0" - } - }, - "node_modules/sass-embedded-all-unknown": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.99.0.tgz", - "integrity": "sha512-qPIRG8Uhjo6/OKyAKixTnwMliTz+t9K6Duk0mx5z+K7n0Ts38NSJz2sjDnc7cA/8V9Lb3q09H38dZ1CLwD+ssw==", - "cpu": [ - "!arm", - "!arm64", - "!riscv64", - "!x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "sass": "1.99.0" - } - }, - "node_modules/sass-embedded-android-arm": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.99.0.tgz", - "integrity": "sha512-EHvJ0C7/VuP78Qr6f8gIUVUmCqIorEQpw2yp3cs3SMg02ZuumlhjXvkTcFBxHmFdFR23vTNk1WnhY6QSeV1nFQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-arm64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.99.0.tgz", - "integrity": "sha512-fNHhdnP23yqqieCbAdym4N47AleSwjbNt6OYIYx4DdACGdtERjQB4iOX/TaKsW034MupfF7SjnAAK8w7Ptldtg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-riscv64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.99.0.tgz", - "integrity": "sha512-4zqDFRvgGDTL5vTHuIhRxUpXFoh0Cy7Gm5Ywk19ASd8Settmd14YdPRZPmMxfgS1GH292PofV1fq1ifiSEJWBw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-android-x64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.99.0.tgz", - "integrity": "sha512-Uk53k/dGYt04RjOL4gFjZ0Z9DH9DKh8IA8WsXUkNqsxerAygoy3zqRBS2zngfE9K2jiOM87q+1R1p87ory9oQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-darwin-arm64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.99.0.tgz", - "integrity": "sha512-u61/7U3IGLqoO6gL+AHeiAtlTPFwJK1+964U8gp45ZN0hzh1yrARf5O1mivXv8NnNgJvbG2wWJbiNZP0lG/lTg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-darwin-x64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.99.0.tgz", - "integrity": "sha512-j/kkk/NcXdIameLezSfXjgCiBkVcA+G60AXrX768/3g0miK1g7M9dj7xOhCb1i7/wQeiEI3rw2LLuO63xRIn4A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-arm": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.99.0.tgz", - "integrity": "sha512-d4IjJZrX2+AwB2YCy1JySwdptJECNP/WfAQLUl8txI3ka8/d3TUI155GtelnoZUkio211PwIeFvvAeZ9RXPQnw==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": "glibc", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-arm64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.99.0.tgz", - "integrity": "sha512-btNcFpItcB56L40n8hDeL7sRSMLDXQ56nB5h2deddJx1n60rpKSElJmkaDGHtpkrY+CTtDRV0FZDjHeTJddYew==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": "glibc", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.99.0.tgz", - "integrity": "sha512-2gvHOupgIw3ytatXT4nFUow71LFbuOZPEwG+HUzcNQDH8ue4Ez8cr03vsv5MDv3lIjOKcXwDvWD980t18MwkoQ==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": "musl", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.99.0.tgz", - "integrity": "sha512-Hi2bt/IrM5P4FBKz6EcHAlniwfpoz9mnTdvSd58y+avA3SANM76upIkAdSayA8ZGwyL3gZokru1AKDPF9lJDNw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": "musl", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-riscv64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.99.0.tgz", - "integrity": "sha512-mKqGvVaJ9rHMqyZsF0kikQe4NO0f4osb67+X6nLhBiVDKvyazQHJ3zJQreNefIE36yL2sjHIclSB//MprzaQDg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": "musl", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.99.0.tgz", - "integrity": "sha512-huhgOMmOc30r7CH7qbRbT9LerSEGSnWuS4CYNOskr9BvNeQp4dIneFufNRGZ7hkOAxUM8DglxIZJN/cyAT95Ew==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": "musl", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-riscv64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.99.0.tgz", - "integrity": "sha512-mevFPIFAVhrH90THifxLfOntFmHtcEKOcdWnep2gJ0X4DVva4AiVIRlQe/7w9JFx5+gnDRE1oaJJkzuFUuYZsA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": "glibc", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-linux-x64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.99.0.tgz", - "integrity": "sha512-9k7IkULqIZdCIVt4Mboryt6vN8Mjmm3EhI1P3mClU5y5i3wLK5ExC3cbVWk047KsID/fvB1RLslqghXJx5BoxA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": "glibc", - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-unknown-all": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.99.0.tgz", - "integrity": "sha512-P7MxiUtL/XzGo3PX0CaB8lNNEFLQWKikPA8pbKytx9ZCLZSDkt2NJcdAbblB/sqMs4AV3EK2NadV8rI/diq3xg==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "!android", - "!darwin", - "!linux", - "!win32" - ], - "dependencies": { - "sass": "1.99.0" - } - }, - "node_modules/sass-embedded-win32-arm64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.99.0.tgz", - "integrity": "sha512-8whpsW7S+uO8QApKfQuc36m3P9EISzbVZOgC79goob4qGy09u8Gz/rYvw8h1prJDSjltpHGhOzBE6LDz7WvzVw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-embedded-win32-x64": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.99.0.tgz", - "integrity": "sha512-ipuOv1R2K4MHeuCEAZGpuUbAgma4gb0sdacyrTjJtMOy/OY9UvWfVlwErdB09KIkp4fPDpQJDJfvYN6bC8jeNg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -4229,22 +3384,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -4252,29 +3391,6 @@ "dev": true, "license": "MIT" }, - "node_modules/sync-child-process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", - "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "sync-message-port": "^1.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/sync-message-port": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz", - "integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -4382,13 +3498,6 @@ "node": ">=18" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/typescript": { "version": "5.9.3", "devOptional": true, @@ -4406,13 +3515,6 @@ "dev": true, "license": "MIT" }, - "node_modules/varint": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", - "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "dev": true, - "license": "MIT" - }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index ddf8a124..514a54ad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,9 +34,7 @@ "@tauri-apps/plugin-fs": "^2.4.0", "@tauri-apps/plugin-notification": "^2.3.0", "@tauri-apps/plugin-opener": "^2.5.0", - "@tauri-apps/plugin-updater": "^2.9.0", "axios": "^1.6.8", - "date-fns": "^3.6.0", "echarts": "^6.0.0", "element-plus": "^2.4.4", "pdfjs-dist": "6.1.200", @@ -51,7 +49,6 @@ "@vitejs/plugin-vue": "^6.0.3", "@vue/test-utils": "^2.4.6", "jsdom": "^26.1.0", - "sass-embedded": "^1.77.0", "typescript": "^5.3.3", "vite": "^7.3.1", "vitest": "^3.2.4", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 73f89354..2aec1b61 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,12 +1,15 @@ - - diff --git a/frontend/src/components/FaqCategoryManager.vue b/frontend/src/components/FaqCategoryManager.vue index adb8614c..c69b159e 100644 --- a/frontend/src/components/FaqCategoryManager.vue +++ b/frontend/src/components/FaqCategoryManager.vue @@ -97,7 +97,7 @@ diff --git a/frontend/src/components/PermissionAccessLogs.test.ts b/frontend/src/components/PermissionAccessLogs.test.ts index 56b82f62..e2e69e0a 100644 --- a/frontend/src/components/PermissionAccessLogs.test.ts +++ b/frontend/src/components/PermissionAccessLogs.test.ts @@ -29,7 +29,7 @@ describe("PermissionAccessLogs", () => { expect(source).toContain("IP访问排行"); expect(source).toContain("ipRankingDialogVisible"); expect(source).toContain("openIpRankingDialog"); - expect(source).toContain("ipRankingPeriodOptions"); + expect(source).toContain('刷新'); @@ -320,8 +320,7 @@ describe("PermissionAccessLogs", () => { const source = readSource(); expect(source).toContain('placeholder="搜索账号、IP、属地、接口或来源"'); - expect(source).toContain("filter-time-presets"); - expect(source).toContain("timeRangePresetOptions"); + expect(source).toContain(' { expect(source).toContain("--filter-control-height: 26px"); expect(source).toContain("--filter-font-size: 12px"); - expect(source).toContain(".filter-time-presets :deep(.el-radio-button__inner)"); + expect(source).toContain('import TimeRangeSelector from "./TimeRangeSelector.vue"'); + expect(source).not.toContain(".filter-time-presets :deep(.el-radio-button__inner)"); expect(source).toContain(".audit-filters :deep(.el-select__wrapper)"); expect(source).toContain(".audit-filters :deep(.el-input__wrapper)"); expect(source).toContain('class="filter-reset"'); diff --git a/frontend/src/components/PermissionAccessLogs.vue b/frontend/src/components/PermissionAccessLogs.vue index debce358..438931fa 100644 --- a/frontend/src/components/PermissionAccessLogs.vue +++ b/frontend/src/components/PermissionAccessLogs.vue @@ -432,8 +432,6 @@ const filters = reactive({ const ROLE_FILTER_KEYS = ["ADMIN", "PM", "CRA", "PV", "QA", "CTA"]; const roleFilterOptions = computed(() => roleOptionsFor(ROLE_FILTER_KEYS)); -const timeRangePresetOptions = TIME_RANGE_PRESETS; -const ipRankingPeriodOptions = TIME_RANGE_PRESETS; const selectedIpRankingPeriod = computed(() => TIME_RANGE_PRESETS.find((period) => period.value === ipRankingPeriod.value) || TIME_RANGE_PRESETS[1]); const clientSourceOptions = [ { value: "web", label: "网页端" }, @@ -1168,31 +1166,6 @@ defineExpose({ refresh, applyTrendFilter }); flex: 0 0 auto; } -.filter-time-presets { - flex-shrink: 0; -} - -.filter-time-presets :deep(.el-radio-button__inner) { - height: var(--filter-control-height); - padding: 0 12px; - border-radius: 0; - font-size: var(--filter-font-size); - font-weight: 500; - line-height: calc(var(--filter-control-height) - 2px); -} - -.filter-time-presets :deep(.el-radio-button:first-child .el-radio-button__inner) { - border-radius: 7px 0 0 7px; -} - -.filter-time-presets :deep(.el-radio-button:last-child .el-radio-button__inner) { - border-radius: 0 7px 7px 0; -} - -.filter-time-presets :deep(.el-radio-button.is-active .el-radio-button__inner) { - font-weight: 600; -} - .filter-role { width: 110px; } diff --git a/frontend/src/components/PermissionIpLocations.test.ts b/frontend/src/components/PermissionIpLocations.test.ts index f1077060..1baafc2f 100644 --- a/frontend/src/components/PermissionIpLocations.test.ts +++ b/frontend/src/components/PermissionIpLocations.test.ts @@ -73,7 +73,9 @@ describe("PermissionIpLocations", () => { it("uses backend coordinates and keeps unmapped locations in the ranking", () => { const source = readSource(); - expect(source).toContain('const mapView = ref<"china" | "flow">("flow");'); + expect(source).toContain('const mapView = ref("flow");'); + expect(source).toContain('load: () => import("../assets/china.json")'); + expect(source).toContain('load: () => import("../assets/world.json")'); expect(source).toContain("item.longitude"); expect(source).toContain("item.latitude"); expect(source).toContain("external_fallback_ip_count"); diff --git a/frontend/src/components/PermissionIpLocations.vue b/frontend/src/components/PermissionIpLocations.vue index 15d90daa..959d1793 100644 --- a/frontend/src/components/PermissionIpLocations.vue +++ b/frontend/src/components/PermissionIpLocations.vue @@ -56,16 +56,20 @@
- +
服务器
- - diff --git a/frontend/src/components/SecurityCenter.vue b/frontend/src/components/SecurityCenter.vue index caa770ed..9349c33d 100644 --- a/frontend/src/components/SecurityCenter.vue +++ b/frontend/src/components/SecurityCenter.vue @@ -429,8 +429,6 @@ const formatLastUpdated = (value: Date) => { return `${value.getFullYear()}/${value.getMonth() + 1}/${value.getDate()} ${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}:${String(value.getSeconds()).padStart(2, "0")}`; }; -const isHighRisk = (event: SecurityAccessLogItem) => event.severity === "CRITICAL" || event.severity === "HIGH"; - const fallbackIpLocation = (ip: string | null) => { if (!ip) return "未知位置"; if (ip === "127.0.0.1" || ip === "::1") return "本机访问"; diff --git a/frontend/src/components/StudySelector.vue b/frontend/src/components/StudySelector.vue deleted file mode 100644 index 7d5460df..00000000 --- a/frontend/src/components/StudySelector.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/frontend/src/components/WebLayout.vue b/frontend/src/components/WebLayout.vue index 76fca5e4..f953e578 100644 --- a/frontend/src/components/WebLayout.vue +++ b/frontend/src/components/WebLayout.vue @@ -408,7 +408,7 @@ import { TEXT } from "../locales"; import { User, Suitcase, House, Calendar, Flag, CircleCheck, Box, Coin, Notebook, Document, ArrowDown, SwitchButton, Files, Key, DataAnalysis, UserFilled, Bell, Setting, - Search, Star, StarFilled, Clock, Monitor, FullScreen, ScaleToOriginal + Search, Star, StarFilled, Monitor, FullScreen, ScaleToOriginal } from "@element-plus/icons-vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { getProjectRoutePermission, hasProjectPermission, projectRouteLandingPaths } from "../utils/projectRoutePermissions"; diff --git a/frontend/src/components/chinaProvinceMap.ts b/frontend/src/components/chinaProvinceMap.ts deleted file mode 100644 index ddea92e7..00000000 --- a/frontend/src/components/chinaProvinceMap.ts +++ /dev/null @@ -1,14 +0,0 @@ -// 地图数据来源:https://geojson.cn/api/china/1.6.3/china.json -// 组件直接导入本地缓存的 GeoJSON,避免运行时依赖外部网络。 - -export const normalizeProvinceName = (value?: string | null) => { - return String(value || "") - .trim() - .replace(/特别行政区$/u, "") - .replace(/维吾尔自治区$/u, "") - .replace(/壮族自治区$/u, "") - .replace(/回族自治区$/u, "") - .replace(/自治区$/u, "") - .replace(/省$/u, "") - .replace(/市$/u, ""); -}; diff --git a/frontend/src/components/selectors/UserSelect.vue b/frontend/src/components/selectors/UserSelect.vue deleted file mode 100644 index 29e2f31a..00000000 --- a/frontend/src/components/selectors/UserSelect.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - diff --git a/frontend/src/dictionaries/utils.ts b/frontend/src/dictionaries/utils.ts index 365ff2bc..2086f300 100644 --- a/frontend/src/dictionaries/utils.ts +++ b/frontend/src/dictionaries/utils.ts @@ -8,8 +8,4 @@ export const createDict = (items: DictItem[]): Dict => { return { items: normalized, get, list }; }; -export const getDictItem = (dict: Dict, value?: string | null) => dict.get(value); export const getDictLabel = (dict: Dict, value?: string | null) => dict.get(value)?.label || value || ""; -export const getDictColor = (dict: Dict, value?: string | null) => dict.get(value)?.color; -export const getDictOptions = (dict: Dict) => - dict.list().map((i) => ({ label: i.label, value: i.value, disabled: false })); diff --git a/frontend/src/locales/index.ts b/frontend/src/locales/index.ts index f465c1bd..5769d346 100644 --- a/frontend/src/locales/index.ts +++ b/frontend/src/locales/index.ts @@ -2,18 +2,4 @@ import { TEXT } from "./zh-CN"; export { TEXT }; -export const formatEnum = ( - value: string | number | null | undefined, - map: Record, - fallback: string = TEXT.common.fallback -) => { - if (value === null || value === undefined || value === "") return fallback; - return map[String(value)] || fallback; -}; - -export const formatBool = (value: boolean | null | undefined) => { - if (value === null || value === undefined) return TEXT.common.fallback; - return value ? TEXT.common.boolean.yes : TEXT.common.boolean.no; -}; - export const requiredMessage = (label: string) => `${label}${TEXT.common.validation.requiredSuffix}`; diff --git a/frontend/src/main.ts b/frontend/src/main.ts index ccbacff7..83195606 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,7 +1,5 @@ import { createApp } from "vue"; import { createPinia } from "pinia"; -import ElementPlus from "element-plus"; -import zhCn from "element-plus/es/locale/lang/zh-cn"; import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; import "element-plus/dist/index.css"; @@ -10,8 +8,11 @@ import "./styles/unified-page.css"; import App from "./App.vue"; import router from "./router"; +import { configureApiRuntimeHooks } from "./api/runtimeHooks"; +import { installElementPlus } from "./plugins/elementPlus"; import { getToken } from "./utils/auth"; import { useStudyStore } from "./store/study"; +import { extendAccessToken, forceLogout, LOGOUT_REASON_AUTH_EXPIRED } from "./session/sessionManager"; import { cleanupTemporaryFiles, initializeSecureSessionStorage, isTauriRuntime, shouldRequireDesktopServerUrl } from "./runtime"; const bootstrap = async () => { @@ -25,7 +26,15 @@ const bootstrap = async () => { const pinia = createPinia(); app.use(pinia); dayjs.locale("zh-cn"); - app.use(ElementPlus, { locale: zhCn }); + installElementPlus(app); + configureApiRuntimeHooks({ + clearInvalidStudyAndNavigate: async () => { + useStudyStore().clearCurrentStudy(); + await router.push("/admin/projects"); + }, + extendAccessToken, + forceAuthExpiredLogout: () => forceLogout(LOGOUT_REASON_AUTH_EXPIRED), + }); // 初始化项目上下文 const studyStore = useStudyStore(); diff --git a/frontend/src/plugins/elementPlus.ts b/frontend/src/plugins/elementPlus.ts new file mode 100644 index 00000000..5ff80dc0 --- /dev/null +++ b/frontend/src/plugins/elementPlus.ts @@ -0,0 +1,132 @@ +import type { App } from "vue"; +import { + ElAlert, + ElAside, + ElAutocomplete, + ElAvatar, + ElBadge, + ElButton, + ElCard, + ElCheckbox, + ElCol, + ElCollapse, + ElCollapseItem, + ElConfigProvider, + ElContainer, + ElDatePicker, + ElDescriptions, + ElDescriptionsItem, + ElDialog, + ElDivider, + ElDrawer, + ElDropdown, + ElDropdownItem, + ElDropdownMenu, + ElEmpty, + ElForm, + ElFormItem, + ElHeader, + ElIcon, + ElImage, + ElInput, + ElInputNumber, + ElLink, + ElLoading, + ElMain, + ElMenu, + ElMenuItem, + ElMenuItemGroup, + ElOption, + ElOptionGroup, + ElPagination, + ElPopover, + ElProgress, + ElRadioButton, + ElRadioGroup, + ElRow, + ElScrollbar, + ElSegmented, + ElSelect, + ElSkeleton, + ElSkeletonItem, + ElSubMenu, + ElSwitch, + ElTabPane, + ElTable, + ElTableColumn, + ElTabs, + ElTag, + ElTimeline, + ElTimelineItem, + ElTooltip, + ElTree, + ElUpload, +} from "element-plus"; + +const components = [ + ElAlert, + ElAside, + ElAutocomplete, + ElAvatar, + ElBadge, + ElButton, + ElCard, + ElCheckbox, + ElCol, + ElCollapse, + ElCollapseItem, + ElConfigProvider, + ElContainer, + ElDatePicker, + ElDescriptions, + ElDescriptionsItem, + ElDialog, + ElDivider, + ElDrawer, + ElDropdown, + ElDropdownItem, + ElDropdownMenu, + ElEmpty, + ElForm, + ElFormItem, + ElHeader, + ElIcon, + ElImage, + ElInput, + ElInputNumber, + ElLink, + ElMain, + ElMenu, + ElMenuItem, + ElMenuItemGroup, + ElOption, + ElOptionGroup, + ElPagination, + ElPopover, + ElProgress, + ElRadioButton, + ElRadioGroup, + ElRow, + ElScrollbar, + ElSegmented, + ElSelect, + ElSkeleton, + ElSkeletonItem, + ElSubMenu, + ElSwitch, + ElTabPane, + ElTable, + ElTableColumn, + ElTabs, + ElTag, + ElTimeline, + ElTimelineItem, + ElTooltip, + ElTree, + ElUpload, +]; + +export const installElementPlus = (app: App): void => { + for (const component of components) app.use(component); + app.use(ElLoading); +}; diff --git a/frontend/src/router.test.ts b/frontend/src/router.test.ts index 4cb2e8f4..b04ad125 100644 --- a/frontend/src/router.test.ts +++ b/frontend/src/router.test.ts @@ -103,7 +103,7 @@ describe("admin project route permissions", () => { const monitoringRouteEnd = source.indexOf("],", monitoringRouteStart); const monitoringRoute = source.slice(monitoringRouteStart, monitoringRouteEnd); - expect(source).toContain('import SystemMonitoringPage from "../views/admin/SystemMonitoringPage.vue";'); + expect(source).toContain('const SystemMonitoringPage = () => import("../views/admin/SystemMonitoringPage.vue");'); expect(source).toContain('path: "system-monitoring"'); expect(monitoringRoute).toContain("component: SystemMonitoringPage"); expect(monitoringRoute).toContain("requiresAdmin: true"); @@ -123,7 +123,7 @@ describe("admin project route permissions", () => { expect(source).toContain('const DESKTOP_PROJECT_ENTRY_PATH = "/desktop/project-entry";'); expect(source).toContain("const resolveWorkbenchEntryPath = (isDesktopRuntime: boolean)"); expect(source).toContain("const isWorkbenchEntryPath = (path: string)"); - expect(source).toContain('import WebWorkbenchEntry from "../views/WebWorkbenchEntry.vue";'); + expect(source).toContain('const WebWorkbenchEntry = () => import("../views/WebWorkbenchEntry.vue");'); expect(source).toContain("path: WEB_WORKBENCH_ENTRY_PATH"); expect(source).toContain('name: "WorkbenchEntry"'); expect(source).toContain("component: WebWorkbenchEntry"); diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 4d79e6b3..8dcb2db2 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -7,65 +7,65 @@ import { findFirstAccessibleProjectPath, getProjectRoutePermission, hasProjectPe import { fetchStudies, fetchStudyDetail } from "../api/studies"; import { fetchApiEndpointPermissions } from "../api/projectPermissions"; import Layout from "../components/Layout.vue"; -import Login from "../views/Login.vue"; -import Register from "../views/Register.vue"; -import ForgotPassword from "../views/ForgotPassword.vue"; -import DesktopServerSettings from "../views/DesktopServerSettings.vue"; -import DesktopProjectEntry from "../views/DesktopProjectEntry.vue"; -import DesktopSessionRestore from "../views/DesktopSessionRestore.vue"; -import WebWorkbenchEntry from "../views/WebWorkbenchEntry.vue"; -import StudyHome from "../views/StudyHome.vue"; -import FaqDetail from "../views/FaqDetail.vue"; -import AuditLogs from "../views/admin/AuditLogs.vue"; -import AdminUsers from "../views/admin/Users.vue"; -import AdminProjects from "../views/admin/Projects.vue"; -import AdminSites from "../views/admin/Sites.vue"; -import PermissionManagement from "../views/admin/PermissionManagement.vue"; -import SystemMonitoringPage from "../views/admin/SystemMonitoringPage.vue"; -import EmailSettings from "../views/admin/EmailSettings.vue"; -import ProjectDetail from "../views/admin/ProjectDetail.vue"; import ProfileSettings from "../views/ProfileSettings.vue"; -import ProjectOverview from "../views/ia/ProjectOverview.vue"; -import ProjectMilestones from "../views/ia/ProjectMilestones.vue"; -import FeeContracts from "../views/fees/ContractFees.vue"; -import FeeContractDetail from "../views/fees/ContractFeeDetail.vue"; -import DrugShipments from "../views/ia/DrugShipments.vue"; -import ShipmentDetail from "../views/drug/ShipmentDetail.vue"; -import MaterialEquipment from "../views/ia/MaterialEquipment.vue"; -import MaterialEquipmentDetail from "../views/materials/MaterialEquipmentDetail.vue"; -import DocumentList from "../views/documents/DocumentList.vue"; -import DocumentDetail from "../views/documents/DocumentDetail.vue"; -import OfficePreviewWorkspace from "../views/OfficePreviewWorkspace.vue"; -import StartupFeasibilityEthics from "../views/ia/StartupFeasibilityEthics.vue"; -import StartupMeetingAuth from "../views/ia/StartupMeetingAuth.vue"; -import SubjectManagement from "../views/ia/SubjectManagement.vue"; -import RiskIssueSae from "../views/ia/RiskIssueSae.vue"; -import RiskIssuePd from "../views/ia/RiskIssuePd.vue"; -import RiskIssueMonitoringVisits from "../views/ia/RiskIssueMonitoringVisits.vue"; -import EtmfPlaceholder from "../views/ia/EtmfPlaceholder.vue"; -import KnowledgeMedicalConsult from "../views/ia/KnowledgeMedicalConsult.vue"; -import Precautions from "../views/ia/Precautions.vue"; -import KnowledgeSupportFiles from "../views/ia/KnowledgeSupportFiles.vue"; -import KnowledgeInstructionFiles from "../views/ia/KnowledgeInstructionFiles.vue"; -import CollaborationLibrary from "../views/knowledge/CollaborationLibrary.vue"; -import CollaborationWorkspace from "../views/knowledge/CollaborationWorkspace.vue"; -import CollaborationShareWorkspace from "../views/knowledge/CollaborationShareWorkspace.vue"; -import FeasibilityForm from "../views/startup/FeasibilityForm.vue"; -import FeasibilityDetail from "../views/startup/FeasibilityDetail.vue"; -import EthicsForm from "../views/startup/EthicsForm.vue"; -import EthicsDetail from "../views/startup/EthicsDetail.vue"; -import KickoffForm from "../views/startup/KickoffForm.vue"; -import KickoffDetail from "../views/startup/KickoffDetail.vue"; -import TrainingDetail from "../views/startup/TrainingDetail.vue"; -import PrecautionForm from "../views/knowledge/PrecautionForm.vue"; -import PrecautionDetail from "../views/knowledge/PrecautionDetail.vue"; -import SubjectForm from "../views/subjects/SubjectForm.vue"; -import SubjectDetail from "../views/subjects/SubjectDetail.vue"; import { TEXT } from "../locales"; import { resolveDesktopRouteRedirect } from "./desktopGuard"; import { isTauriRuntime } from "../runtime"; import { DESKTOP_SESSION_RESTORE_PATH, shouldPreserveDesktopSessionOnAuthCheckFailure } from "../session/authRecovery"; +const Login = () => import("../views/Login.vue"); +const Register = () => import("../views/Register.vue"); +const ForgotPassword = () => import("../views/ForgotPassword.vue"); +const DesktopServerSettings = () => import("../views/DesktopServerSettings.vue"); +const DesktopProjectEntry = () => import("../views/DesktopProjectEntry.vue"); +const DesktopSessionRestore = () => import("../views/DesktopSessionRestore.vue"); +const WebWorkbenchEntry = () => import("../views/WebWorkbenchEntry.vue"); +const FaqDetail = () => import("../views/FaqDetail.vue"); +const AuditLogs = () => import("../views/admin/AuditLogs.vue"); +const AdminUsers = () => import("../views/admin/Users.vue"); +const AdminProjects = () => import("../views/admin/Projects.vue"); +const AdminSites = () => import("../views/admin/Sites.vue"); +const PermissionManagement = () => import("../views/admin/PermissionManagement.vue"); +const SystemMonitoringPage = () => import("../views/admin/SystemMonitoringPage.vue"); +const EmailSettings = () => import("../views/admin/EmailSettings.vue"); +const ProjectDetail = () => import("../views/admin/ProjectDetail.vue"); +const ProjectOverview = () => import("../views/ia/ProjectOverview.vue"); +const ProjectMilestones = () => import("../views/ia/ProjectMilestones.vue"); +const FeeContracts = () => import("../views/fees/ContractFees.vue"); +const FeeContractDetail = () => import("../views/fees/ContractFeeDetail.vue"); +const DrugShipments = () => import("../views/ia/DrugShipments.vue"); +const ShipmentDetail = () => import("../views/drug/ShipmentDetail.vue"); +const MaterialEquipment = () => import("../views/ia/MaterialEquipment.vue"); +const MaterialEquipmentDetail = () => import("../views/materials/MaterialEquipmentDetail.vue"); +const DocumentList = () => import("../views/documents/DocumentList.vue"); +const DocumentDetail = () => import("../views/documents/DocumentDetail.vue"); +const OfficePreviewWorkspace = () => import("../views/OfficePreviewWorkspace.vue"); +const StartupFeasibilityEthics = () => import("../views/ia/StartupFeasibilityEthics.vue"); +const StartupMeetingAuth = () => import("../views/ia/StartupMeetingAuth.vue"); +const SubjectManagement = () => import("../views/ia/SubjectManagement.vue"); +const RiskIssueSae = () => import("../views/ia/RiskIssueSae.vue"); +const RiskIssuePd = () => import("../views/ia/RiskIssuePd.vue"); +const RiskIssueMonitoringVisits = () => import("../views/ia/RiskIssueMonitoringVisits.vue"); +const EtmfPlaceholder = () => import("../views/ia/EtmfPlaceholder.vue"); +const KnowledgeMedicalConsult = () => import("../views/ia/KnowledgeMedicalConsult.vue"); +const Precautions = () => import("../views/ia/Precautions.vue"); +const KnowledgeSupportFiles = () => import("../views/ia/KnowledgeSupportFiles.vue"); +const KnowledgeInstructionFiles = () => import("../views/ia/KnowledgeInstructionFiles.vue"); +const CollaborationLibrary = () => import("../views/knowledge/CollaborationLibrary.vue"); +const CollaborationWorkspace = () => import("../views/knowledge/CollaborationWorkspace.vue"); +const CollaborationShareWorkspace = () => import("../views/knowledge/CollaborationShareWorkspace.vue"); +const FeasibilityForm = () => import("../views/startup/FeasibilityForm.vue"); +const FeasibilityDetail = () => import("../views/startup/FeasibilityDetail.vue"); +const EthicsForm = () => import("../views/startup/EthicsForm.vue"); +const EthicsDetail = () => import("../views/startup/EthicsDetail.vue"); +const KickoffForm = () => import("../views/startup/KickoffForm.vue"); +const KickoffDetail = () => import("../views/startup/KickoffDetail.vue"); +const TrainingDetail = () => import("../views/startup/TrainingDetail.vue"); +const PrecautionForm = () => import("../views/knowledge/PrecautionForm.vue"); +const PrecautionDetail = () => import("../views/knowledge/PrecautionDetail.vue"); +const SubjectForm = () => import("../views/subjects/SubjectForm.vue"); +const SubjectDetail = () => import("../views/subjects/SubjectDetail.vue"); + const SYSTEM_PERMISSION_READ = "system:permissions:read"; const SYSTEM_PERMISSION_PROJECT_CONFIG = "system:permissions:project_config"; const WEB_WORKBENCH_ENTRY_PATH = "/workbench"; diff --git a/frontend/src/state-machine/index.ts b/frontend/src/state-machine/index.ts index c581bab7..f57e8c7b 100644 --- a/frontend/src/state-machine/index.ts +++ b/frontend/src/state-machine/index.ts @@ -1,20 +1,5 @@ import type { StateMachine } from "./types"; -export const getStateLabel = (machine: StateMachine, stateValue?: string | null) => { - if (!stateValue) return ""; - return machine.states[stateValue]?.label || stateValue; -}; - -export const getStateColor = (machine: StateMachine, stateValue?: string | null) => { - if (!stateValue) return undefined; - return machine.states[stateValue]?.color; -}; - -export const getAvailableActions = (machine: StateMachine, currentState?: string | null) => { - if (!currentState) return []; - return Object.values(machine.actions).filter((a) => a.from.includes(currentState)); -}; - export const canDoAction = (machine: StateMachine, actionKey: string, currentState?: string | null) => { if (!currentState) return false; const action = machine.actions[actionKey]; diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index fad4824c..f193b66e 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -149,11 +149,6 @@ export interface PasswordResetTokenRequest { password: string; } -export interface AdminUserListResponse { - items: UserInfo[]; - total: number; -} - export interface Study { id: string; code: string; diff --git a/frontend/src/types/documents.ts b/frontend/src/types/documents.ts index eebdf4fc..1e60331e 100644 --- a/frontend/src/types/documents.ts +++ b/frontend/src/types/documents.ts @@ -59,27 +59,6 @@ export interface DocumentVersion { can_actions?: string[]; } -export interface WorkflowNode { - id: string; - node_order: number; - role: string; - name?: string | null; - is_required: boolean; - created_at: string; -} - -export interface WorkflowTemplate { - id: string; - trial_id?: string | null; - name: string; - description?: string | null; - is_active: boolean; - created_by?: string | null; - created_at: string; - updated_at: string; - nodes?: WorkflowNode[]; -} - export interface DistributionStats { total: number; received: number; @@ -104,18 +83,6 @@ export interface Distribution { stats?: DistributionStats; } -export interface Acknowledgement { - id: string; - distribution_id: string; - user_id: string; - site_id?: string | null; - ack_type: "RECEIVED" | "READ" | "TRAINED"; - due_at?: string | null; - acked_at?: string | null; - note?: string | null; - created_at: string; -} - export interface DocumentDetail extends DocumentSummary { description?: string | null; version_timeline: DocumentVersion[]; diff --git a/frontend/src/types/etmf.ts b/frontend/src/types/etmf.ts index 3c14dd45..4fac448d 100644 --- a/frontend/src/types/etmf.ts +++ b/frontend/src/types/etmf.ts @@ -1,4 +1,4 @@ -import type { DocumentScopeType, DocumentSummary } from "./documents"; +import type { DocumentScopeType } from "./documents"; export type EtmfNodeStatus = "NOT_REQUIRED" | "MISSING" | "UPLOADED" | "EFFECTIVE" | "INACTIVE"; @@ -35,18 +35,6 @@ export interface EtmfNodeCreatePayload { is_active?: boolean; } -export interface EtmfNodeUpdatePayload { - parent_id?: string | null; - code?: string; - name?: string; - description?: string | null; - scope_type?: DocumentScopeType; - required?: boolean; - expected_doc_type?: string | null; - sort_order?: number; - is_active?: boolean; -} - export type EtmfDocumentCreatePayload = { trial_id: string; site_id?: string | null; @@ -57,7 +45,3 @@ export type EtmfDocumentCreatePayload = { owner_id?: string | null; description?: string | null; }; - -export interface EtmfNodeDocumentList { - documents: DocumentSummary[]; -} diff --git a/frontend/src/types/visits.ts b/frontend/src/types/visits.ts deleted file mode 100644 index 60b5b531..00000000 --- a/frontend/src/types/visits.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface VisitLostItem { - visit_id: string; - subject_id: string; - subject_no: string; - site_id?: string | null; - visit_code: string; - status: string; - updated_at: string; -} diff --git a/frontend/src/utils/display/index.ts b/frontend/src/utils/display/index.ts index c925e1c1..8d4c2a37 100644 --- a/frontend/src/utils/display/index.ts +++ b/frontend/src/utils/display/index.ts @@ -41,11 +41,6 @@ export const displayUser = ( return (name as string) || displayFallback; }; -export const displayEntity = (entityMap: Record, id?: string | null) => { - if (!id) return displayFallback; - return entityMap[id] || displayFallback; -}; - export const displayDate = (value?: string | number | Date | null) => { if (!value) return displayFallback; const date = new Date(value); diff --git a/frontend/src/utils/permission.ts b/frontend/src/utils/permission.ts index 10da05ec..d292937b 100644 --- a/frontend/src/utils/permission.ts +++ b/frontend/src/utils/permission.ts @@ -5,59 +5,6 @@ import { TEXT } from "../locales"; import { getProjectRole, isSystemAdmin } from "./roles"; import { isApiPermissionAllowed } from "./apiPermissionValue"; -const PERMISSIONS: Record = { - "subject.create": ["ADMIN"], - "subject.enroll": ["ADMIN"], - "subject.complete": ["ADMIN"], - "subject.drop": ["ADMIN"], - "subject.delete": ["ADMIN"], - "ae.create": ["ADMIN"], - "ae.close": ["ADMIN"], - "faq.edit": ["ADMIN"], - "faq.update": ["ADMIN"], - "faq.delete": ["ADMIN"], - "faq.create": ["ADMIN"], - "faq.reply": ["ADMIN"], - "faq.reply.delete": ["ADMIN"], - "faq.category.read": ["ADMIN"], - "faq.category.create": ["ADMIN"], - "faq.category.update": ["ADMIN"], - "faq.category.delete": ["ADMIN"], - "precautions.create": ["ADMIN"], - "precautions.update": ["ADMIN"], - "precautions.delete": ["ADMIN"], - "startup.initiation.create": ["ADMIN"], - "startup.initiation.update": ["ADMIN"], - "startup.initiation.delete": ["ADMIN"], - "startup.ethics.create": ["ADMIN"], - "startup.ethics.update": ["ADMIN"], - "startup.ethics.delete": ["ADMIN"], - "startup.auth.create": ["ADMIN"], - "startup.auth.update": ["ADMIN"], - "startup.auth.delete": ["ADMIN"], - "project.milestones.update": ["ADMIN"], - "project.members.list": ["ADMIN"], - "project.members.candidates": ["ADMIN"], - "project.members.create": ["ADMIN"], - "project.members.update": ["ADMIN"], - "project.members.delete": ["ADMIN"], - "site.manage": ["ADMIN"], - "site.cra.bind": ["ADMIN"], - "fees.contract.create": ["ADMIN"], - "fees.contract.update": ["ADMIN"], - "fees.contract.delete": ["ADMIN"], - "documents.read": ["ADMIN"], - "documents.create": ["ADMIN"], - "documents.update": ["ADMIN"], - "documents.delete": ["ADMIN"], - "collaboration.read": ["ADMIN"], - "collaboration.create": ["ADMIN"], - "collaboration.edit": ["ADMIN"], - "collaboration.manage": ["ADMIN"], - "collaboration.export": ["ADMIN"], - "collaboration.delete": ["ADMIN"], -}; - const REASONS: Record = { "subject.enroll": TEXT.modules.permissions.subjectEnroll, "subject.complete": TEXT.modules.permissions.subjectComplete, diff --git a/frontend/src/views/DesktopProjectEntry.vue b/frontend/src/views/DesktopProjectEntry.vue index c50ec56b..d4d6ec2b 100644 --- a/frontend/src/views/DesktopProjectEntry.vue +++ b/frontend/src/views/DesktopProjectEntry.vue @@ -174,9 +174,6 @@ const userDisplayName = computed( () => auth.user?.full_name || auth.user?.username || auth.user?.email || TEXT.common.labels.userFallback, ); const userInitial = computed(() => userDisplayName.value.charAt(0).toUpperCase() || TEXT.common.labels.userInitialFallback); -const entrySubtitle = computed(() => - canEnterManagement.value ? "进入系统管理,或选择一个项目开始桌面工作。" : "请选择本次要进入的项目。", -); const statusLabel = (status: string | undefined) => status ? TEXT.enums.projectStatus[status as keyof typeof TEXT.enums.projectStatus] || status : "未知"; diff --git a/frontend/src/views/Register.vue b/frontend/src/views/Register.vue index 2c9044f9..4b8cc1ef 100644 --- a/frontend/src/views/Register.vue +++ b/frontend/src/views/Register.vue @@ -368,7 +368,7 @@ import { sendRegisterEmailCode, verifyRegisterEmailCode, } from "../api/auth"; -import type { ApiError, RegisterRequest } from "../types/api"; +import type { ApiError } from "../types/api"; import { TEXT, requiredMessage } from "../locales"; import { privacyPolicySections, serviceTermsSections } from "../content/authProtocol"; diff --git a/frontend/src/views/StudyHome.vue b/frontend/src/views/StudyHome.vue deleted file mode 100644 index afc6181c..00000000 --- a/frontend/src/views/StudyHome.vue +++ /dev/null @@ -1,371 +0,0 @@ - - - - - diff --git a/frontend/src/views/admin/ApiPermissions.test.ts b/frontend/src/views/admin/ApiPermissions.test.ts deleted file mode 100644 index ff3bb3c3..00000000 --- a/frontend/src/views/admin/ApiPermissions.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; -import { mount } from "@vue/test-utils"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; -import ApiPermissions from "@/views/admin/ApiPermissions.vue"; -import { createPinia, setActivePinia } from "pinia"; - -vi.mock("vue-router", async () => { - const actual = await vi.importActual("vue-router"); - return { - ...actual, - useRoute: () => ({ params: { id: "study-1" } }), - }; -}); - -vi.mock("@/api/projectPermissions", () => ({ - fetchApiEndpointPermissions: vi.fn().mockResolvedValue({ data: {} }), - updateApiEndpointPermissions: vi.fn().mockResolvedValue({ data: {} }), - fetchPermissionMetrics: vi.fn().mockResolvedValue({ data: {} }), - fetchCacheStats: vi.fn().mockResolvedValue({ data: {} }), - fetchPermissionAlerts: vi.fn().mockResolvedValue({ data: { total: 0, alerts: [] } }), - fetchPermissionHealth: vi.fn().mockResolvedValue({ data: {} }), - resetPermissionMetrics: vi.fn().mockResolvedValue({ data: undefined }), -})); - -describe("ApiPermissions.vue", () => { - beforeEach(() => { - Object.defineProperty(window, "localStorage", { - value: { - getItem: vi.fn(() => null), - setItem: vi.fn(), - removeItem: vi.fn(), - clear: vi.fn(), - }, - configurable: true, - }); - Object.defineProperty(globalThis, "localStorage", { - value: window.localStorage, - configurable: true, - }); - setActivePinia(createPinia()); - }); - - it("renders permission management page", () => { - const wrapper = mount(ApiPermissions, { - global: { - stubs: { - ApiEndpointPermissions: true, - PermissionMonitoring: true, - }, - }, - }); - - expect(wrapper.find(".permission-page").exists()).toBe(true); - expect(wrapper.find(".permission-title h2").text()).toBe("权限管理"); - }); - - it("renders tabs", () => { - const source = readFileSync(resolve(__dirname, "./ApiPermissions.vue"), "utf8"); - - expect(source).toContain('label="角色权限"'); - expect(source).not.toContain('label="权限监控"'); - expect(source).not.toContain(" { - const source = readFileSync(resolve(__dirname, "./ApiPermissions.vue"), "utf8"); - - expect(source).toContain(':disabled="!dirty"'); - }); -}); diff --git a/frontend/src/views/admin/ApiPermissions.vue b/frontend/src/views/admin/ApiPermissions.vue deleted file mode 100644 index fe3268d8..00000000 --- a/frontend/src/views/admin/ApiPermissions.vue +++ /dev/null @@ -1,229 +0,0 @@ - - - - - diff --git a/frontend/src/views/admin/PermissionManagement.test.ts b/frontend/src/views/admin/PermissionManagement.test.ts index c1ea0443..c251892e 100644 --- a/frontend/src/views/admin/PermissionManagement.test.ts +++ b/frontend/src/views/admin/PermissionManagement.test.ts @@ -56,7 +56,8 @@ describe("permission management custom roles", () => { expect(source).toContain("const roleDisplayName = (row: PermissionTemplate) => row.name;"); expect(source).toContain("await loadPermissionData();"); expect(source).toContain('Object.keys(assignableRoleLabels.value)[0] || ""'); - expect(source).toContain('role === "ADMIN"'); + expect(source).toContain("const canAssignProjectRole"); + expect(source).toContain("(ROLE_RANK[role] ?? 0) < ROLE_RANK.PM"); expect(source).not.toContain("const ALL_ROLES = ["); expect(source).not.toContain("const ROLE_LABELS"); }); diff --git a/frontend/src/views/admin/PermissionManagement.vue b/frontend/src/views/admin/PermissionManagement.vue index 4c320ac3..5a06f334 100644 --- a/frontend/src/views/admin/PermissionManagement.vue +++ b/frontend/src/views/admin/PermissionManagement.vue @@ -655,7 +655,7 @@ - - - - diff --git a/frontend/src/views/admin/SiteCraBinding.vue b/frontend/src/views/admin/SiteCraBinding.vue deleted file mode 100644 index 1b1e6ca1..00000000 --- a/frontend/src/views/admin/SiteCraBinding.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - diff --git a/frontend/src/views/documents/DocumentList.vue b/frontend/src/views/documents/DocumentList.vue index f42039e8..f3cd30b0 100644 --- a/frontend/src/views/documents/DocumentList.vue +++ b/frontend/src/views/documents/DocumentList.vue @@ -231,7 +231,6 @@ import { Plus } from "@element-plus/icons-vue"; import { fetchDocuments, createDocument, deleteDocument, updateDocument } from "../../api/documents"; import { fetchSites } from "../../api/sites"; import { useStudyStore } from "../../store/study"; -import { useAuthStore } from "../../store/auth"; import { usePermission } from "../../utils/permission"; import { useDrawerDirtyGuard } from "../../utils/drawerDirtyGuard"; import type { DocumentSummary } from "../../types/documents"; @@ -244,7 +243,6 @@ import { isTauriRuntime } from "../../runtime"; const route = useRoute(); const router = useRouter(); const study = useStudyStore(); -const auth = useAuthStore(); const { can } = usePermission(); const loading = ref(false); const items = ref([]); @@ -284,9 +282,6 @@ const siteActiveMap = computed(() => { }); return map; }); -const isAdmin = computed(() => { - return !!auth.user?.is_admin; -}); const canCreate = computed(() => can("documents.create")); const canUpdate = computed(() => can("documents.update")); const canDelete = computed(() => can("documents.delete")); diff --git a/frontend/src/views/ia/EtmfPlaceholder.vue b/frontend/src/views/ia/EtmfPlaceholder.vue index 732d5644..19b939ad 100644 --- a/frontend/src/views/ia/EtmfPlaceholder.vue +++ b/frontend/src/views/ia/EtmfPlaceholder.vue @@ -395,12 +395,6 @@ const selectNode = async (node: EtmfTreeNode) => { await loadNodeDocuments(); }; -const resetFilters = () => { - filters.siteId = ""; - filters.status = ""; - loadNodeDocuments(); -}; - const openNodeDialog = () => { if (!canCreate.value) { ElMessage.warning("权限不足"); diff --git a/frontend/src/views/ia/RiskIssues.vue b/frontend/src/views/ia/RiskIssues.vue deleted file mode 100644 index d8648291..00000000 --- a/frontend/src/views/ia/RiskIssues.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/frontend/src/views/startup/TrainingForm.vue b/frontend/src/views/startup/TrainingForm.vue deleted file mode 100644 index db16be94..00000000 --- a/frontend/src/views/startup/TrainingForm.vue +++ /dev/null @@ -1,192 +0,0 @@ - - - - - diff --git a/frontend/src/views/subjects/VisitEditorDrawer.vue b/frontend/src/views/subjects/VisitEditorDrawer.vue index 36bc4de2..4feec469 100644 --- a/frontend/src/views/subjects/VisitEditorDrawer.vue +++ b/frontend/src/views/subjects/VisitEditorDrawer.vue @@ -16,7 +16,7 @@
- +
@@ -99,7 +99,7 @@