diff --git a/adminSystem/src/api/amt.ts b/adminSystem/src/api/amt.ts new file mode 100644 index 0000000..f618627 --- /dev/null +++ b/adminSystem/src/api/amt.ts @@ -0,0 +1,279 @@ +import request from '@/utils/http' + +// 扫描 API +export const scanApi = { + // 启动扫描 + startScan(networkSegment: string, subnetMask: string) { + return request.post({ + url: '/api/scan/start', + params: { networkSegment, subnetMask } + }) + }, + + // 获取扫描状态 + getScanStatus(taskId: string) { + return request.get({ + url: `/api/scan/status/${taskId}` + }) + }, + + // 取消扫描 + cancelScan(taskId: string) { + return request.post({ + url: `/api/scan/cancel/${taskId}` + }) + } +} + +// 设备 API +export const deviceApi = { + // 获取所有设备 + getAllDevices() { + return request.get({ + url: '/api/devices' + }) + }, + + // 搜索设备 + searchDevices(keyword: string) { + return request.get({ + url: '/api/devices/search', + params: { keyword } + }) + }, + + // 删除设备 + deleteDevice(id: number) { + return request.del({ + url: `/api/devices/${id}`, + showSuccessMessage: true + }) + }, + + // 检测所有设备在线状态 + checkAllDevicesStatus() { + return request.get({ + url: '/api/devices/status' + }) + }, + + // 检测单个设备在线状态 + checkDeviceStatus(id: number) { + return request.get({ + url: `/api/devices/${id}/status` + }) + } +} + +// 硬件信息 API +export const hardwareApi = { + // 获取设备硬件信息 + getHardwareInfo(deviceId: number, refresh = false) { + return request.get({ + url: `/api/hardware-info/${deviceId}`, + params: { refresh } + }) + }, + + // 批量获取硬件信息 + getBatchHardwareInfo(deviceIds: number[], refresh = false) { + return request.post({ + url: '/api/hardware-info/batch', + params: { deviceIds, refresh } + }) + } +} + +// 电源管理 API +export const powerApi = { + // 获取电源状态 + getPowerState(deviceId: number) { + return request.get({ + url: `/api/power/${deviceId}/state` + }) + }, + + // 开机 + powerOn(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/power-on` + }) + }, + + // 关机(优雅关机) + powerOff(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/power-off` + }) + }, + + // 强制关机 + forceOff(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/force-off` + }) + }, + + // 重启(优雅重启) + restart(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/restart` + }) + }, + + // 强制重启 + forceRestart(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/force-restart` + }) + }, + + // 电源循环 + powerCycle(deviceId: number) { + return request.post({ + url: `/api/power/${deviceId}/power-cycle` + }) + } +} + +// AMT 凭据 API +export const credentialApi = { + // 获取所有凭据 + getAllCredentials() { + return request.get({ + url: '/api/credentials' + }) + }, + + // 创建凭据 + createCredential(data: { name: string; username: string; password: string; isDefault?: boolean; description?: string }) { + return request.post({ + url: '/api/credentials', + params: data, + showSuccessMessage: true + }) + }, + + // 更新凭据 + updateCredential(id: number, data: { name?: string; username?: string; password?: string; isDefault?: boolean; description?: string }) { + return request.put({ + url: `/api/credentials/${id}`, + params: data, + showSuccessMessage: true + }) + }, + + // 删除凭据 + deleteCredential(id: number) { + return request.del({ + url: `/api/credentials/${id}`, + showSuccessMessage: true + }) + } +} + +// Windows 凭据 API +export const windowsCredentialsApi = { + // 获取所有凭据 + getAll() { + return request.get({ + url: '/api/windowscredentials' + }) + }, + + // 创建凭据 + create(data: { name: string; username: string; password: string; domain?: string; isDefault?: boolean; description?: string }) { + return request.post({ + url: '/api/windowscredentials', + params: data, + showSuccessMessage: true + }) + }, + + // 更新凭据 + update(id: number, data: { name?: string; username?: string; password?: string; domain?: string; isDefault?: boolean; description?: string }) { + return request.put({ + url: `/api/windowscredentials/${id}`, + params: data, + showSuccessMessage: true + }) + }, + + // 删除凭据 + delete(id: number) { + return request.del({ + url: `/api/windowscredentials/${id}`, + showSuccessMessage: true + }) + }, + + // 设置默认凭据 + setDefault(id: number) { + return request.post({ + url: `/api/windowscredentials/${id}/set-default`, + showSuccessMessage: true + }) + } +} + +// 远程桌面 API +export const remoteDesktopApi = { + // 直接连接(需要凭据) + connect(deviceId: number, credentials: { username: string; password: string; domain?: string }) { + return request.post({ + url: `/api/remotedesktop/connect/${deviceId}`, + params: credentials + }) + }, + + // 生成访问 Token + generateToken(deviceId: number, options: { credentialId?: number; expiresInMinutes?: number; maxUseCount?: number; note?: string } = {}) { + return request.post({ + url: `/api/remotedesktop/generate-token/${deviceId}`, + params: options + }) + }, + + // 通过 Token 连接 + connectByToken(token: string) { + return request.get({ + url: `/api/remotedesktop/connect-by-token/${token}` + }) + }, + + // 验证 Token + validateToken(token: string) { + return request.get({ + url: `/api/remotedesktop/validate-token/${token}` + }) + }, + + // 获取设备的所有 Token + getDeviceTokens(deviceId: number) { + return request.get({ + url: `/api/remotedesktop/list-tokens/${deviceId}` + }) + }, + + // 撤销 Token + revokeToken(tokenId: number) { + return request.del({ + url: `/api/remotedesktop/revoke-token/${tokenId}`, + showSuccessMessage: true + }) + }, + + // 清理过期 Token + cleanupTokens() { + return request.post({ + url: '/api/remotedesktop/cleanup-tokens' + }) + }, + + // 测试 Guacamole 连接 + test() { + return request.get({ + url: '/api/remotedesktop/test' + }) + } +} diff --git a/adminSystem/src/router/guards/beforeEach.ts b/adminSystem/src/router/guards/beforeEach.ts index 0571e4f..a035740 100644 --- a/adminSystem/src/router/guards/beforeEach.ts +++ b/adminSystem/src/router/guards/beforeEach.ts @@ -129,12 +129,23 @@ async function handleRouteGuard( NProgress.start() } - // 1. 检查登录状态 + // 1. 检查是否为静态路由(不需要权限验证) + if (isStaticRoute(to.path)) { + // 静态路由直接放行,不需要登录验证和权限验证 + if (to.matched.length > 0) { + setWorktab(to) + setPageTitle(to) + next() + return + } + } + + // 2. 检查登录状态 if (!handleLoginStatus(to, userStore, next)) { return } - // 2. 处理动态路由注册 + // 3. 处理动态路由注册 if (!routeRegistry?.isRegistered() && userStore.isLogin) { await handleDynamicRoutes(to, next, router) return diff --git a/adminSystem/src/router/routes/staticRoutes.ts b/adminSystem/src/router/routes/staticRoutes.ts index 334d0c2..629eaf3 100644 --- a/adminSystem/src/router/routes/staticRoutes.ts +++ b/adminSystem/src/router/routes/staticRoutes.ts @@ -11,13 +11,13 @@ import { AppRouteRecordRaw } from '@/utils/router' * 2、静态路由不管是否登录都可以访问 */ export const staticRoutes: AppRouteRecordRaw[] = [ - // 不需要登录就能访问的路由示例 - // { - // path: '/welcome', - // name: 'WelcomeStatic', - // component: () => import('@views/dashboard/console/index.vue'), - // meta: { title: 'menus.dashboard.title' } - // }, + // 远程桌面访问页面(不需要登录) + { + path: '/remote/:token', + name: 'RemoteAccess', + component: () => import('@views/remote/index.vue'), + meta: { title: '远程桌面', isHideTab: true } + }, { path: '/auth/login', name: 'Login', diff --git a/adminSystem/src/views/amt/credentials.vue b/adminSystem/src/views/amt/credentials.vue new file mode 100644 index 0000000..52347ce --- /dev/null +++ b/adminSystem/src/views/amt/credentials.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/adminSystem/src/views/amt/devices.vue b/adminSystem/src/views/amt/devices.vue new file mode 100644 index 0000000..dfb0126 --- /dev/null +++ b/adminSystem/src/views/amt/devices.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/adminSystem/src/views/amt/modules/hardware-info-modal.vue b/adminSystem/src/views/amt/modules/hardware-info-modal.vue new file mode 100644 index 0000000..ac40ad2 --- /dev/null +++ b/adminSystem/src/views/amt/modules/hardware-info-modal.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/adminSystem/src/views/amt/modules/remote-desktop-modal.vue b/adminSystem/src/views/amt/modules/remote-desktop-modal.vue new file mode 100644 index 0000000..848b7df --- /dev/null +++ b/adminSystem/src/views/amt/modules/remote-desktop-modal.vue @@ -0,0 +1,305 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/result/fail/index.vue b/art-design-pro-3.0.1/src/views/result/fail/index.vue new file mode 100644 index 0000000..8fe2583 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/result/fail/index.vue @@ -0,0 +1,28 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/result/success/index.vue b/art-design-pro-3.0.1/src/views/result/success/index.vue new file mode 100644 index 0000000..ae57aba --- /dev/null +++ b/art-design-pro-3.0.1/src/views/result/success/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/safeguard/server/index.vue b/art-design-pro-3.0.1/src/views/safeguard/server/index.vue new file mode 100644 index 0000000..368ac27 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/safeguard/server/index.vue @@ -0,0 +1,168 @@ + + + + diff --git a/art-design-pro-3.0.1/src/views/system/menu/index.vue b/art-design-pro-3.0.1/src/views/system/menu/index.vue new file mode 100644 index 0000000..973b1e7 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/menu/index.vue @@ -0,0 +1,479 @@ + + + + diff --git a/art-design-pro-3.0.1/src/views/system/menu/modules/menu-dialog.vue b/art-design-pro-3.0.1/src/views/system/menu/modules/menu-dialog.vue new file mode 100644 index 0000000..f512301 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/menu/modules/menu-dialog.vue @@ -0,0 +1,384 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/system/nested/menu1/index.vue b/art-design-pro-3.0.1/src/views/system/nested/menu1/index.vue new file mode 100644 index 0000000..9eb2bab --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/nested/menu1/index.vue @@ -0,0 +1,5 @@ + diff --git a/art-design-pro-3.0.1/src/views/system/nested/menu2/index.vue b/art-design-pro-3.0.1/src/views/system/nested/menu2/index.vue new file mode 100644 index 0000000..8da183f --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/nested/menu2/index.vue @@ -0,0 +1,5 @@ + diff --git a/art-design-pro-3.0.1/src/views/system/nested/menu3/index.vue b/art-design-pro-3.0.1/src/views/system/nested/menu3/index.vue new file mode 100644 index 0000000..fc7d496 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/nested/menu3/index.vue @@ -0,0 +1,5 @@ + diff --git a/art-design-pro-3.0.1/src/views/system/nested/menu3/menu3-2/index.vue b/art-design-pro-3.0.1/src/views/system/nested/menu3/menu3-2/index.vue new file mode 100644 index 0000000..7387a03 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/nested/menu3/menu3-2/index.vue @@ -0,0 +1,5 @@ + diff --git a/art-design-pro-3.0.1/src/views/system/role/index.vue b/art-design-pro-3.0.1/src/views/system/role/index.vue new file mode 100644 index 0000000..aca447e --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/role/index.vue @@ -0,0 +1,242 @@ + + + + diff --git a/art-design-pro-3.0.1/src/views/system/role/modules/role-edit-dialog.vue b/art-design-pro-3.0.1/src/views/system/role/modules/role-edit-dialog.vue new file mode 100644 index 0000000..46ff9b1 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/role/modules/role-edit-dialog.vue @@ -0,0 +1,162 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/system/role/modules/role-permission-dialog.vue b/art-design-pro-3.0.1/src/views/system/role/modules/role-permission-dialog.vue new file mode 100644 index 0000000..3691ac8 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/role/modules/role-permission-dialog.vue @@ -0,0 +1,254 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/system/role/modules/role-search.vue b/art-design-pro-3.0.1/src/views/system/role/modules/role-search.vue new file mode 100644 index 0000000..1d59cee --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/role/modules/role-search.vue @@ -0,0 +1,121 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/system/user-center/index.vue b/art-design-pro-3.0.1/src/views/system/user-center/index.vue new file mode 100644 index 0000000..ab52f00 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/user-center/index.vue @@ -0,0 +1,247 @@ + + + + diff --git a/art-design-pro-3.0.1/src/views/system/user/index.vue b/art-design-pro-3.0.1/src/views/system/user/index.vue new file mode 100644 index 0000000..fb794d7 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/user/index.vue @@ -0,0 +1,261 @@ + + + + + + + + diff --git a/art-design-pro-3.0.1/src/views/system/user/modules/user-dialog.vue b/art-design-pro-3.0.1/src/views/system/user/modules/user-dialog.vue new file mode 100644 index 0000000..03cab4f --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/user/modules/user-dialog.vue @@ -0,0 +1,143 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/system/user/modules/user-search.vue b/art-design-pro-3.0.1/src/views/system/user/modules/user-search.vue new file mode 100644 index 0000000..e097720 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/system/user/modules/user-search.vue @@ -0,0 +1,112 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/template/banners/index.vue b/art-design-pro-3.0.1/src/views/template/banners/index.vue new file mode 100644 index 0000000..2702156 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/banners/index.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/art-design-pro-3.0.1/src/views/template/calendar/index.vue b/art-design-pro-3.0.1/src/views/template/calendar/index.vue new file mode 100644 index 0000000..5a35403 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/calendar/index.vue @@ -0,0 +1,288 @@ + + + + + diff --git a/art-design-pro-3.0.1/src/views/template/cards/index.vue b/art-design-pro-3.0.1/src/views/template/cards/index.vue new file mode 100644 index 0000000..4308754 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/cards/index.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/art-design-pro-3.0.1/src/views/template/charts/index.vue b/art-design-pro-3.0.1/src/views/template/charts/index.vue new file mode 100644 index 0000000..1f5dd13 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/charts/index.vue @@ -0,0 +1,355 @@ + + + + + + diff --git a/art-design-pro-3.0.1/src/views/template/chat/index.vue b/art-design-pro-3.0.1/src/views/template/chat/index.vue new file mode 100644 index 0000000..d7d5c09 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/chat/index.vue @@ -0,0 +1,452 @@ + + + + diff --git a/art-design-pro-3.0.1/src/views/template/map/index.vue b/art-design-pro-3.0.1/src/views/template/map/index.vue new file mode 100644 index 0000000..3171d83 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/map/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/template/pricing/index.vue b/art-design-pro-3.0.1/src/views/template/pricing/index.vue new file mode 100644 index 0000000..0845293 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/template/pricing/index.vue @@ -0,0 +1,134 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/context-menu/index.vue b/art-design-pro-3.0.1/src/views/widgets/context-menu/index.vue new file mode 100644 index 0000000..03a0a1a --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/context-menu/index.vue @@ -0,0 +1,136 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/count-to/index.vue b/art-design-pro-3.0.1/src/views/widgets/count-to/index.vue new file mode 100644 index 0000000..70a0620 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/count-to/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/drag/index.vue b/art-design-pro-3.0.1/src/views/widgets/drag/index.vue new file mode 100644 index 0000000..61379f7 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/drag/index.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/excel/index.vue b/art-design-pro-3.0.1/src/views/widgets/excel/index.vue new file mode 100644 index 0000000..47b15b2 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/excel/index.vue @@ -0,0 +1,151 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/fireworks/index.vue b/art-design-pro-3.0.1/src/views/widgets/fireworks/index.vue new file mode 100644 index 0000000..e2026a2 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/fireworks/index.vue @@ -0,0 +1,116 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/icon/index.vue b/art-design-pro-3.0.1/src/views/widgets/icon/index.vue new file mode 100644 index 0000000..333afd4 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/icon/index.vue @@ -0,0 +1,110 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/image-crop/index.vue b/art-design-pro-3.0.1/src/views/widgets/image-crop/index.vue new file mode 100644 index 0000000..27376bc --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/image-crop/index.vue @@ -0,0 +1,59 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/qrcode/index.vue b/art-design-pro-3.0.1/src/views/widgets/qrcode/index.vue new file mode 100644 index 0000000..1d4c8c5 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/qrcode/index.vue @@ -0,0 +1,130 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/text-scroll/index.vue b/art-design-pro-3.0.1/src/views/widgets/text-scroll/index.vue new file mode 100644 index 0000000..7cfefe5 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/text-scroll/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/video/index.vue b/art-design-pro-3.0.1/src/views/widgets/video/index.vue new file mode 100644 index 0000000..1a83891 --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/video/index.vue @@ -0,0 +1,32 @@ + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/wang-editor/index.vue b/art-design-pro-3.0.1/src/views/widgets/wang-editor/index.vue new file mode 100644 index 0000000..25b42aa --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/wang-editor/index.vue @@ -0,0 +1,537 @@ + + + + + diff --git a/art-design-pro-3.0.1/src/views/widgets/watermark/index.vue b/art-design-pro-3.0.1/src/views/widgets/watermark/index.vue new file mode 100644 index 0000000..956d8ce --- /dev/null +++ b/art-design-pro-3.0.1/src/views/widgets/watermark/index.vue @@ -0,0 +1,75 @@ + + + diff --git a/art-design-pro-3.0.1/tsconfig.json b/art-design-pro-3.0.1/tsconfig.json new file mode 100644 index 0000000..4331962 --- /dev/null +++ b/art-design-pro-3.0.1/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "types": ["vite/client", "node", "element-plus/global"], + "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"], + "@views/*": ["src/views/*"], + "@imgs/*": ["src/assets/images/*"], + "@icons/*": ["src/assets/icons/*"], + "@utils/*": ["src/utils/*"], + "@stores/*": ["src/store/*"], + "@plugins/*": ["src/plugins/*"], + "@styles/*": ["src/assets/styles/*"] + } + }, + "include": ["src/**/*", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "exclude": ["node_modules", "dist", "**/*.js"] +} diff --git a/art-design-pro-3.0.1/vite.config.ts b/art-design-pro-3.0.1/vite.config.ts new file mode 100644 index 0000000..c2ef072 --- /dev/null +++ b/art-design-pro-3.0.1/vite.config.ts @@ -0,0 +1,156 @@ +import { defineConfig, loadEnv } from 'vite' +import vue from '@vitejs/plugin-vue' +import path from 'path' +import { fileURLToPath } from 'url' +import vueDevTools from 'vite-plugin-vue-devtools' +import viteCompression from 'vite-plugin-compression' +import Components from 'unplugin-vue-components/vite' +import AutoImport from 'unplugin-auto-import/vite' +import ElementPlus from 'unplugin-element-plus/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' +import tailwindcss from '@tailwindcss/vite' +// import { visualizer } from 'rollup-plugin-visualizer' + +export default ({ mode }: { mode: string }) => { + const root = process.cwd() + const env = loadEnv(mode, root) + const { VITE_VERSION, VITE_PORT, VITE_BASE_URL, VITE_API_URL, VITE_API_PROXY_URL } = env + + console.log(`🚀 API_URL = ${VITE_API_URL}`) + console.log(`🚀 VERSION = ${VITE_VERSION}`) + + return defineConfig({ + define: { + __APP_VERSION__: JSON.stringify(VITE_VERSION) + }, + base: VITE_BASE_URL, + server: { + port: Number(VITE_PORT), + proxy: { + '/api': { + target: VITE_API_PROXY_URL, + changeOrigin: true + } + }, + host: true + }, + // 路径别名 + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + '@views': resolvePath('src/views'), + '@imgs': resolvePath('src/assets/images'), + '@icons': resolvePath('src/assets/icons'), + '@utils': resolvePath('src/utils'), + '@stores': resolvePath('src/store'), + '@styles': resolvePath('src/assets/styles') + } + }, + build: { + target: 'es2015', + outDir: 'dist', + chunkSizeWarningLimit: 2000, + minify: 'terser', + terserOptions: { + compress: { + // 生产环境去除 console + drop_console: true, + // 生产环境去除 debugger + drop_debugger: true + } + }, + dynamicImportVarsOptions: { + warnOnError: true, + exclude: [], + include: ['src/views/**/*.vue'] + } + }, + plugins: [ + vue(), + tailwindcss(), + // 自动按需导入 API + AutoImport({ + imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'], + dts: 'src/types/import/auto-imports.d.ts', + resolvers: [ElementPlusResolver()], + eslintrc: { + enabled: true, + filepath: './.auto-import.json', + globalsPropValue: true + } + }), + // 自动按需导入组件 + Components({ + dts: 'src/types/import/components.d.ts', + resolvers: [ElementPlusResolver()] + }), + // 按需定制主题配置 + ElementPlus({ + useSource: true + }), + // 压缩 + viteCompression({ + verbose: false, // 是否在控制台输出压缩结果 + disable: false, // 是否禁用 + algorithm: 'gzip', // 压缩算法 + ext: '.gz', // 压缩后的文件名后缀 + threshold: 10240, // 只有大小大于该值的资源会被处理 10240B = 10KB + deleteOriginFile: false // 压缩后是否删除原文件 + }), + vueDevTools() + // 打包分析 + // visualizer({ + // open: true, + // gzipSize: true, + // brotliSize: true, + // filename: 'dist/stats.html' // 分析图生成的文件名及路径 + // }), + ], + // 依赖预构建:避免运行时重复请求与转换,提升首次加载速度 + optimizeDeps: { + include: [ + 'echarts/core', + 'echarts/charts', + 'echarts/components', + 'echarts/renderers', + 'xlsx', + 'xgplayer', + 'crypto-js', + 'file-saver', + 'vue-img-cutter', + 'element-plus/es', + 'element-plus/es/components/*/style/css', + 'element-plus/es/components/*/style/index' + ] + }, + css: { + preprocessorOptions: { + // sass variable and mixin + scss: { + additionalData: ` + @use "@styles/core/el-light.scss" as *; + @use "@styles/core/mixin.scss" as *; + ` + } + }, + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove() + } + } + } + } + ] + } + } + }) +} + +function resolvePath(paths: string) { + return path.resolve(__dirname, paths) +} diff --git a/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs b/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs index 4b9ff71..284ed13 100644 --- a/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs +++ b/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs @@ -282,6 +282,13 @@ defineOptions({{ name: '{componentName}' }}) new() { Id = 1, Name = "Dashboard", Path = "/dashboard", Component = "/index/index", Title = "menus.dashboard.title", Icon = "ri:pie-chart-line", Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, new() { Id = 2, ParentId = 1, Name = "Console", Path = "console", Component = "/dashboard/console", Title = "menus.dashboard.console", KeepAlive = false, Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + // AMT 设备管理菜单(系统内置) + new() { Id = 5, Name = "AmtManage", Path = "/amt", Component = "/index/index", Title = "设备管理", Icon = "ri:computer-line", Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 6, ParentId = 5, Name = "AmtScan", Path = "scan", Component = "/amt/scan", Title = "网络扫描", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 7, ParentId = 5, Name = "AmtDevices", Path = "devices", Component = "/amt/devices", Title = "设备列表", KeepAlive = true, Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 8, ParentId = 5, Name = "AmtCredentials", Path = "credentials", Component = "/amt/credentials", Title = "AMT凭据", KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 9, ParentId = 5, Name = "WindowsCredentials", Path = "windows-credentials", Component = "/amt/windows-credentials", Title = "Windows凭据", KeepAlive = true, Sort = 4, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + // 系统管理菜单(系统内置) new() { Id = 10, Name = "System", Path = "/system", Component = "/index/index", Title = "menus.system.title", Icon = "ri:user-3-line", Sort = 99, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, new() { Id = 11, ParentId = 10, Name = "User", Path = "user", Component = "/system/user", Title = "menus.system.user", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, @@ -319,6 +326,68 @@ defineOptions({{ name: '{componentName}' }}) return Ok(ApiResponse.Success(null, "菜单已重置为默认配置")); } + + /// + /// 添加 AMT 设备管理菜单(如果不存在) + /// + [Authorize] + [HttpPost("api/menu/seed-amt")] + public async Task>> SeedAmtMenus() + { + // 检查是否已存在 AMT 菜单 + if (await _context.Menus.AnyAsync(m => m.Name == "AmtManage")) + { + return Ok(ApiResponse.Success(null, "AMT菜单已存在")); + } + + // 获取当前最大 ID + var maxId = await _context.Menus.MaxAsync(m => (int?)m.Id) ?? 0; + var startId = Math.Max(maxId + 1, 5); + + var amtMenus = new List + { + new() { Name = "AmtManage", Path = "/amt", Component = "/index/index", Title = "设备管理", Icon = "ri:computer-line", Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + }; + + _context.Menus.AddRange(amtMenus); + await _context.SaveChangesAsync(); + + // 获取刚创建的目录菜单 ID + var amtManageId = amtMenus[0].Id; + + // 添加子菜单 + var childMenus = new List + { + new() { ParentId = amtManageId, Name = "AmtScan", Path = "scan", Component = "/amt/scan", Title = "网络扫描", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { ParentId = amtManageId, Name = "AmtDevices", Path = "devices", Component = "/amt/devices", Title = "设备列表", KeepAlive = true, Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { ParentId = amtManageId, Name = "AmtCredentials", Path = "credentials", Component = "/amt/credentials", Title = "AMT凭据", KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { ParentId = amtManageId, Name = "WindowsCredentials", Path = "windows-credentials", Component = "/amt/windows-credentials", Title = "Windows凭据", KeepAlive = true, Sort = 4, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + }; + + _context.Menus.AddRange(childMenus); + await _context.SaveChangesAsync(); + + // 为超级管理员和管理员分配菜单权限 + var superRole = await _context.Roles.FirstOrDefaultAsync(r => r.RoleCode == "R_SUPER"); + var adminRole = await _context.Roles.FirstOrDefaultAsync(r => r.RoleCode == "R_ADMIN"); + + var allNewMenuIds = new List { amtManageId }; + allNewMenuIds.AddRange(childMenus.Select(m => m.Id)); + + var roleMenus = new List(); + foreach (var menuId in allNewMenuIds) + { + if (superRole != null) + roleMenus.Add(new RoleMenu { RoleId = superRole.Id, MenuId = menuId }); + if (adminRole != null) + roleMenus.Add(new RoleMenu { RoleId = adminRole.Id, MenuId = menuId }); + } + + _context.RoleMenus.AddRange(roleMenus); + await _context.SaveChangesAsync(); + + return Ok(ApiResponse.Success(null, "AMT菜单已添加成功")); + } } /// diff --git a/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs b/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs index 58b1973..f22c082 100644 --- a/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs +++ b/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs @@ -25,24 +25,34 @@ public class RemoteDesktopController : ControllerBase _logger = logger; } + /// + /// 生成远程访问 Token(管理员使用) + /// [HttpPost("generate-token/{deviceId}")] - public async Task> GenerateToken( + public async Task>> GenerateToken( long deviceId, [FromBody] GenerateTokenRequest request) { var device = await _context.AmtDevices.FindAsync(deviceId); if (device == null) - return NotFound(new { error = "设备不存在" }); + return Ok(ApiResponse.Fail(404, "设备不存在")); - WindowsCredential? credential = request.CredentialId.HasValue - ? await _context.WindowsCredentials.FindAsync(request.CredentialId.Value) - : await _context.WindowsCredentials.FirstOrDefaultAsync(c => c.IsDefault); + WindowsCredential? credential = null; + if (request.CredentialId.HasValue) + { + credential = await _context.WindowsCredentials.FindAsync(request.CredentialId.Value); + } + else + { + credential = await _context.WindowsCredentials.FirstOrDefaultAsync(c => c.IsDefault); + } if (credential == null) - return BadRequest(new { error = "请先配置 Windows 凭据" }); + return Ok(ApiResponse.Fail(400, "请先配置 Windows 凭据")); var token = GenerateRandomToken(); var expiresAt = DateTime.UtcNow.AddMinutes(request.ExpiresInMinutes ?? 30); + var accessToken = new RemoteAccessToken { Token = token, @@ -57,22 +67,27 @@ public class RemoteDesktopController : ControllerBase await _context.SaveChangesAsync(); var baseUrl = $"{Request.Scheme}://{Request.Host}"; + var accessUrl = $"{baseUrl}/remote/{token}"; + _logger.LogInformation("Generated remote access token for device {Ip}, expires at {ExpiresAt}", device.IpAddress, expiresAt); - return Ok(new GenerateTokenResponse + return Ok(ApiResponse.Success(new GenerateTokenResponse { Success = true, Token = token, - AccessUrl = $"{baseUrl}/remote/{token}", + AccessUrl = accessUrl, ExpiresAt = expiresAt, MaxUseCount = accessToken.MaxUseCount, DeviceIp = device.IpAddress - }); + }, "Token 生成成功")); } + /// + /// 通过 Token 连接远程桌面 + /// [HttpGet("connect-by-token/{token}")] - public async Task> ConnectByToken(string token) + public async Task>> ConnectByToken(string token) { var accessToken = await _context.RemoteAccessTokens .Include(t => t.Device) @@ -80,13 +95,13 @@ public class RemoteDesktopController : ControllerBase .FirstOrDefaultAsync(t => t.Token == token); if (accessToken == null) - return NotFound(new { error = "无效的访问链接" }); + return Ok(ApiResponse.Fail(404, "无效的访问链接")); if (!accessToken.IsValid()) - return BadRequest(new { error = "访问链接已过期或已达到使用次数上限" }); + return Ok(ApiResponse.Fail(400, "访问链接已过期或已达到使用次数上限")); if (accessToken.Device == null || accessToken.WindowsCredential == null) - return BadRequest(new { error = "设备或凭据信息不完整" }); + return Ok(ApiResponse.Fail(400, "设备或凭据信息不完整")); accessToken.UseCount++; accessToken.UsedAt = DateTime.UtcNow; @@ -94,7 +109,7 @@ public class RemoteDesktopController : ControllerBase var guacToken = await _guacamoleService.GetAuthTokenAsync(); if (string.IsNullOrEmpty(guacToken)) - return StatusCode(503, new { error = "无法连接到 Guacamole 服务" }); + return Ok(ApiResponse.Fail(503, "无法连接到 Guacamole 服务")); var connectionName = $"AMT-{accessToken.Device.IpAddress}"; var connectionId = await _guacamoleService.CreateOrGetConnectionAsync( @@ -102,44 +117,50 @@ public class RemoteDesktopController : ControllerBase accessToken.WindowsCredential.Username, accessToken.WindowsCredential.Password); if (string.IsNullOrEmpty(connectionId)) - return StatusCode(500, new { error = "创建远程连接失败" }); + return Ok(ApiResponse.Fail(500, "创建远程连接失败")); var connectionUrl = await _guacamoleService.GetConnectionUrlAsync(guacToken, connectionId); - return Ok(new RemoteDesktopResponse + return Ok(ApiResponse.Success(new RemoteDesktopResponse { Success = true, ConnectionUrl = connectionUrl, ConnectionId = connectionId, Token = guacToken, DeviceIp = accessToken.Device.IpAddress - }); + })); } + /// + /// 验证 Token 是否有效 + /// [HttpGet("validate-token/{token}")] - public async Task> ValidateToken(string token) + public async Task>> ValidateToken(string token) { var accessToken = await _context.RemoteAccessTokens .Include(t => t.Device) .FirstOrDefaultAsync(t => t.Token == token); if (accessToken == null) - return Ok(new ValidateTokenResponse { Valid = false, Error = "无效的访问链接" }); + return Ok(ApiResponse.Success(new ValidateTokenResponse { Valid = false, Error = "无效的访问链接" })); if (!accessToken.IsValid()) - return Ok(new ValidateTokenResponse { Valid = false, Error = "访问链接已过期或已达到使用次数上限" }); + return Ok(ApiResponse.Success(new ValidateTokenResponse { Valid = false, Error = "访问链接已过期或已达到使用次数上限" })); - return Ok(new ValidateTokenResponse + return Ok(ApiResponse.Success(new ValidateTokenResponse { Valid = true, DeviceIp = accessToken.Device?.IpAddress, ExpiresAt = accessToken.ExpiresAt, RemainingUses = accessToken.MaxUseCount > 0 ? accessToken.MaxUseCount - accessToken.UseCount : -1 - }); + })); } + /// + /// 获取设备的所有有效 Token + /// [HttpGet("list-tokens/{deviceId}")] - public async Task>> GetDeviceTokens(long deviceId) + public async Task>>> GetDeviceTokens(long deviceId) { var tokens = await _context.RemoteAccessTokens .Where(t => t.DeviceId == deviceId && t.ExpiresAt > DateTime.UtcNow) @@ -156,66 +177,78 @@ public class RemoteDesktopController : ControllerBase }) .ToListAsync(); - return Ok(tokens); + return Ok(ApiResponse>.Success(tokens)); } + /// + /// 撤销 Token + /// [HttpDelete("revoke-token/{tokenId}")] - public async Task RevokeToken(long tokenId) + public async Task>> RevokeToken(long tokenId) { var token = await _context.RemoteAccessTokens.FindAsync(tokenId); if (token == null) - return NotFound(new { error = "Token 不存在" }); + return Ok(ApiResponse.Fail(404, "Token 不存在")); _context.RemoteAccessTokens.Remove(token); await _context.SaveChangesAsync(); - return Ok(new { success = true }); + return Ok(ApiResponse.Success(null, "Token 已撤销")); } + /// + /// 清理过期 Token + /// [HttpPost("cleanup-tokens")] - public async Task CleanupExpiredTokens() + public async Task>> CleanupExpiredTokens() { var count = await _context.RemoteAccessTokens .Where(t => t.ExpiresAt < DateTime.UtcNow) .ExecuteDeleteAsync(); - return Ok(new { success = true, deletedCount = count }); + return Ok(ApiResponse.Success(new CleanupTokensResponse { DeletedCount = count }, "已清理 " + count + " 个过期 Token")); } + /// + /// 直接连接(需要输入凭据) + /// [HttpPost("connect/{deviceId}")] - public async Task> Connect(long deviceId, [FromBody] RdpCredentials credentials) + public async Task>> Connect(long deviceId, [FromBody] RdpCredentials credentials) { var device = await _context.AmtDevices.FindAsync(deviceId); if (device == null) - return NotFound(new { error = "设备不存在" }); + return Ok(ApiResponse.Fail(404, "设备不存在")); var guacToken = await _guacamoleService.GetAuthTokenAsync(); if (string.IsNullOrEmpty(guacToken)) - return StatusCode(503, new { error = "无法连接到 Guacamole 服务" }); + return Ok(ApiResponse.Fail(503, "无法连接到 Guacamole 服务")); var connectionName = $"AMT-{device.IpAddress}"; var connectionId = await _guacamoleService.CreateOrGetConnectionAsync( guacToken, connectionName, device.IpAddress, credentials.Username, credentials.Password); if (string.IsNullOrEmpty(connectionId)) - return StatusCode(500, new { error = "创建远程连接失败" }); + return Ok(ApiResponse.Fail(500, "创建远程连接失败")); var connectionUrl = await _guacamoleService.GetConnectionUrlAsync(guacToken, connectionId); - return Ok(new RemoteDesktopResponse + return Ok(ApiResponse.Success(new RemoteDesktopResponse { Success = true, ConnectionUrl = connectionUrl, ConnectionId = connectionId, Token = guacToken - }); + })); } + /// + /// 测试 Guacamole 连接 + /// [HttpGet("test")] - public async Task TestConnection() + public async Task>> TestConnection() { var token = await _guacamoleService.GetAuthTokenAsync(); if (string.IsNullOrEmpty(token)) - return StatusCode(503, new { success = false, error = "无法连接到 Guacamole 服务" }); - return Ok(new { success = true, message = "Guacamole 服务连接正常" }); + return Ok(ApiResponse.Fail(503, "无法连接到 Guacamole 服务")); + return Ok(ApiResponse.Success(new TestConnectionResponse { Success = true, Message = "Guacamole 服务连接正常" })); } private static string GenerateRandomToken() @@ -284,4 +317,15 @@ public class RemoteDesktopResponse public string? Error { get; set; } } +public class CleanupTokensResponse +{ + public int DeletedCount { get; set; } +} + +public class TestConnectionResponse +{ + public bool Success { get; set; } + public string Message { get; set; } = string.Empty; +} + #endregion diff --git a/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs b/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs index 271cd67..17acb71 100644 --- a/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs +++ b/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs @@ -105,6 +105,13 @@ public static class DbSeeder new() { Id = 1, Name = "Dashboard", Path = "/dashboard", Component = "/index/index", Title = "menus.dashboard.title", Icon = "ri:pie-chart-line", Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, new() { Id = 2, ParentId = 1, Name = "Console", Path = "console", Component = "/dashboard/console", Title = "menus.dashboard.console", KeepAlive = false, Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + // AMT 设备管理菜单(系统内置) + new() { Id = 5, Name = "AmtManage", Path = "/amt", Component = "/index/index", Title = "设备管理", Icon = "ri:computer-line", Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 6, ParentId = 5, Name = "AmtScan", Path = "scan", Component = "/amt/scan", Title = "网络扫描", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 7, ParentId = 5, Name = "AmtDevices", Path = "devices", Component = "/amt/devices", Title = "设备列表", KeepAlive = true, Sort = 2, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 8, ParentId = 5, Name = "AmtCredentials", Path = "credentials", Component = "/amt/credentials", Title = "AMT凭据", KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 9, ParentId = 5, Name = "WindowsCredentials", Path = "windows-credentials", Component = "/amt/windows-credentials", Title = "Windows凭据", KeepAlive = true, Sort = 4, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + // 系统管理菜单 - 与前端 system.ts 匹配(系统内置) new() { Id = 10, Name = "System", Path = "/system", Component = "/index/index", Title = "menus.system.title", Icon = "ri:user-3-line", Sort = 99, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, new() { Id = 11, ParentId = 10, Name = "User", Path = "user", Component = "/system/user", Title = "menus.system.user", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, diff --git a/backend-csharp/AmtScanner.Api/add_amt_menus.sql b/backend-csharp/AmtScanner.Api/add_amt_menus.sql new file mode 100644 index 0000000..07777e2 --- /dev/null +++ b/backend-csharp/AmtScanner.Api/add_amt_menus.sql @@ -0,0 +1,48 @@ +-- 添加 AMT 设备管理菜单 +-- 先检查是否已存在,避免重复插入 + +-- 插入目录菜单 +INSERT INTO Menus (Id, ParentId, Name, Path, Component, Title, Icon, Sort, Roles, IsHide, KeepAlive, IsSystem, CreatedAt, UpdatedAt) +SELECT 5, NULL, 'AmtManage', '/amt', '/index/index', '设备管理', 'ri:computer-line', 2, 'R_SUPER,R_ADMIN', 0, 0, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM Menus WHERE Id = 5 OR Name = 'AmtManage'); + +-- 插入子菜单:网络扫描 +INSERT INTO Menus (Id, ParentId, Name, Path, Component, Title, Icon, Sort, Roles, IsHide, KeepAlive, IsSystem, CreatedAt, UpdatedAt) +SELECT 6, 5, 'AmtScan', 'scan', '/amt/scan', '网络扫描', NULL, 1, 'R_SUPER,R_ADMIN', 0, 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM Menus WHERE Id = 6 OR Name = 'AmtScan'); + +-- 插入子菜单:设备列表 +INSERT INTO Menus (Id, ParentId, Name, Path, Component, Title, Icon, Sort, Roles, IsHide, KeepAlive, IsSystem, CreatedAt, UpdatedAt) +SELECT 7, 5, 'AmtDevices', 'devices', '/amt/devices', '设备列表', NULL, 2, 'R_SUPER,R_ADMIN', 0, 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM Menus WHERE Id = 7 OR Name = 'AmtDevices'); + +-- 插入子菜单:AMT凭据 +INSERT INTO Menus (Id, ParentId, Name, Path, Component, Title, Icon, Sort, Roles, IsHide, KeepAlive, IsSystem, CreatedAt, UpdatedAt) +SELECT 8, 5, 'AmtCredentials', 'credentials', '/amt/credentials', 'AMT凭据', NULL, 3, 'R_SUPER,R_ADMIN', 0, 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM Menus WHERE Id = 8 OR Name = 'AmtCredentials'); + +-- 插入子菜单:Windows凭据 +INSERT INTO Menus (Id, ParentId, Name, Path, Component, Title, Icon, Sort, Roles, IsHide, KeepAlive, IsSystem, CreatedAt, UpdatedAt) +SELECT 9, 5, 'WindowsCredentials', 'windows-credentials', '/amt/windows-credentials', 'Windows凭据', NULL, 4, 'R_SUPER,R_ADMIN', 0, 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM Menus WHERE Id = 9 OR Name = 'WindowsCredentials'); + +-- 为超级管理员和管理员分配新菜单权限 +-- 获取角色ID +SET @superRoleId = (SELECT Id FROM Roles WHERE RoleCode = 'R_SUPER' LIMIT 1); +SET @adminRoleId = (SELECT Id FROM Roles WHERE RoleCode = 'R_ADMIN' LIMIT 1); + +-- 为超级管理员分配菜单 +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@superRoleId, 5); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@superRoleId, 6); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@superRoleId, 7); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@superRoleId, 8); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@superRoleId, 9); + +-- 为管理员分配菜单 +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@adminRoleId, 5); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@adminRoleId, 6); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@adminRoleId, 7); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@adminRoleId, 8); +INSERT IGNORE INTO RoleMenus (RoleId, MenuId) VALUES (@adminRoleId, 9); + +SELECT '✅ AMT菜单已添加完成' AS Result; diff --git a/backend-csharp/adminSystem/src/views/testdir/mypage.vue b/backend-csharp/adminSystem/src/views/testdir/mypage.vue new file mode 100644 index 0000000..bc80006 --- /dev/null +++ b/backend-csharp/adminSystem/src/views/testdir/mypage.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/backend-csharp/adminSystem/src/views/testdir/mypage2.vue b/backend-csharp/adminSystem/src/views/testdir/mypage2.vue new file mode 100644 index 0000000..d9eca33 --- /dev/null +++ b/backend-csharp/adminSystem/src/views/testdir/mypage2.vue @@ -0,0 +1,21 @@ + + + + +