From ecf78bc7278f908c50eedc597a69c9491434ffea Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sat, 21 Mar 2026 17:06:54 +0800 Subject: [PATCH 1/5] fix --- pnpm-lock.yaml | 54 +++ src/api/ops/dcControl.ts | 22 +- src/locale/en-US.ts | 1 + src/locale/zh-CN.ts | 1 + src/router/menu-data.ts | 17 +- src/router/routes/modules/ops.ts | 10 + .../system-settings/license-center/index.vue | 379 ++++++++++++++++++ 7 files changed, 480 insertions(+), 4 deletions(-) create mode 100644 src/views/ops/pages/system-settings/license-center/index.vue diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fc39cf..141b4d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: vue-router: specifier: '5' version: 5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)) + vue-web-terminal: + specifier: ^3.4.1 + version: 3.4.1(typescript@5.9.3) devDependencies: '@arco-plugins/vite-vue': specifier: ^1.4.6 @@ -1551,6 +1554,9 @@ packages: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} + clipboard@2.0.11: + resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1865,6 +1871,9 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + delegate@3.2.0: + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} + dir-glob@2.2.2: resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==} engines: {node: '>=4'} @@ -2411,6 +2420,9 @@ packages: engines: {node: '>=0.6.0'} hasBin: true + good-listener@1.2.2: + resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3707,6 +3719,9 @@ packages: scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + select@1.1.2: + resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} + semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -4056,6 +4071,9 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -4344,6 +4362,11 @@ packages: peerDependencies: vue: ^3.0.0 + vue-json-viewer@3.0.4: + resolution: {integrity: sha512-pnC080rTub6YjccthVSNQod2z9Sl5IUUq46srXtn6rxwhW8QM4rlYn+CTSLFKXWfw+N3xv77Cioxw7B4XUKIbQ==} + peerDependencies: + vue: ^3.2.2 + vue-router@5.0.3: resolution: {integrity: sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw==} peerDependencies: @@ -4365,6 +4388,9 @@ packages: peerDependencies: typescript: '>=5.0.0' + vue-web-terminal@3.4.1: + resolution: {integrity: sha512-+gU28qClqvIZQlzokcvDS2tbFpGfIJKIPc6dvLm2VYX110c6NOh7mV1YrcUESnaE5VQ9DgxqtIbr1YraEA/GRQ==} + vue@3.5.29: resolution: {integrity: sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==} peerDependencies: @@ -5884,6 +5910,12 @@ snapshots: slice-ansi: 8.0.0 string-width: 8.2.0 + clipboard@2.0.11: + dependencies: + good-listener: 1.2.2 + select: 1.1.2 + tiny-emitter: 2.1.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -6178,6 +6210,8 @@ snapshots: delayed-stream@1.0.0: {} + delegate@3.2.0: {} + dir-glob@2.2.2: dependencies: path-type: 3.0.0 @@ -6882,6 +6916,10 @@ snapshots: dependencies: minimist: 1.2.8 + good-listener@1.2.2: + dependencies: + delegate: 3.2.0 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -8164,6 +8202,8 @@ snapshots: scule@1.3.0: {} + select@1.1.2: {} + semver@5.7.2: {} semver@6.3.1: {} @@ -8630,6 +8670,8 @@ snapshots: text-table@0.2.0: {} + tiny-emitter@2.1.0: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -8953,6 +8995,11 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.29(typescript@5.9.3) + vue-json-viewer@3.0.4(vue@3.5.29(typescript@5.9.3)): + dependencies: + clipboard: 2.0.11 + vue: 3.5.29(typescript@5.9.3) + vue-router@5.0.3(@vue/compiler-sfc@3.5.29)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)): dependencies: '@babel/generator': 7.29.1 @@ -8983,6 +9030,13 @@ snapshots: '@vue/language-core': 3.2.5 typescript: 5.9.3 + vue-web-terminal@3.4.1(typescript@5.9.3): + dependencies: + vue: 3.5.29(typescript@5.9.3) + vue-json-viewer: 3.0.4(vue@3.5.29(typescript@5.9.3)) + transitivePeerDependencies: + - typescript + vue@3.5.29(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.29 diff --git a/src/api/ops/dcControl.ts b/src/api/ops/dcControl.ts index 5eea408..7935f02 100644 --- a/src/api/ops/dcControl.ts +++ b/src/api/ops/dcControl.ts @@ -1,5 +1,24 @@ import { request } from "@/api/request"; +/** 许可证配置(与 DC-Control `LicenceConfig` / 接口 `data` 一致;字段按实际响应可能部分缺失) */ +export interface LicenceConfig { + title?: string + version?: string + company_name?: string + create_time?: string + expire_time?: string + machine_code?: string + max_database?: number + max_middleware?: number + max_pc?: number + max_server?: number + max_client?: number + max_user?: number + max_role?: number + max_permission?: number + max_menu?: number +} + /** 获取 采集器 */ export const fetchCollectors = (data: { page: number, size: number, keyword?: string }) => request.get("/DC-Control/v1/collectors", { params: data }); @@ -19,4 +38,5 @@ export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collec export const fetchCollectorStatistics = () => request.get("/DC-Control/v1/statistics"); /** 获取 许可证信息 */ -export const fetchLicenseInfo = () => request.get("/DC-Control/v1/license"); +export const fetchLicenseInfo = () => + request.get<{ code?: number; data?: LicenceConfig; message?: string }>("/DC-Control/v1/license"); diff --git a/src/locale/en-US.ts b/src/locale/en-US.ts index dbab2ce..7b7c509 100644 --- a/src/locale/en-US.ts +++ b/src/locale/en-US.ts @@ -32,6 +32,7 @@ export default { 'menu.ops.systemSettings': 'System Settings', 'menu.ops.systemSettings.menuManagement': 'Menu Management', 'menu.ops.systemSettings.systemLogs': 'System Logs', + 'menu.ops.systemSettings.licenseCenter': 'License Center', 'menu.ops.webTest': 'Web Test', 'menu.ops.report': 'Report Management', 'menu.ops.report.history': 'Report History', diff --git a/src/locale/zh-CN.ts b/src/locale/zh-CN.ts index ba4f588..d73d41d 100644 --- a/src/locale/zh-CN.ts +++ b/src/locale/zh-CN.ts @@ -32,6 +32,7 @@ export default { 'menu.ops.systemSettings': '系统设置', 'menu.ops.systemSettings.menuManagement': '菜单管理', 'menu.ops.systemSettings.systemLogs': '系统日志', + 'menu.ops.systemSettings.licenseCenter': '许可授权中心', 'menu.ops.webTest': '网页测试', 'menu.ops.report': '报表管理', 'menu.ops.report.history': '报表历史', diff --git a/src/router/menu-data.ts b/src/router/menu-data.ts index 8ac3bad..1b33029 100644 --- a/src/router/menu-data.ts +++ b/src/router/menu-data.ts @@ -181,6 +181,11 @@ function extractRelativePath(childPath: string, parentPath: string): string { * @param parentIsFull 父级菜单的 is_full 字段 * @returns 子路由配置数组 */ +/** 服务端未配置 component 时按 menu_path 绑定视图(避免误用 redirect 导致白屏) */ +const MENU_PATH_COMPONENT_FALLBACK: { test: (fullPath: string) => boolean; component: string }[] = [ + { test: (p) => p.includes('license-center'), component: 'ops/pages/system-settings/license-center' }, +] + function transformChildRoutes( children: ServerMenuItem[], parentComponent?: string, @@ -188,10 +193,16 @@ function transformChildRoutes( parentIsFull?: boolean ): AppRouteRecordRaw[] { return children.map((child) => { - // 优先使用子菜单自己的 component,否则继承父级的 component - const componentPath = child.component || parentComponent - // 计算子路由的相对路径 + // 计算子路由的相对路径(需先于 component 解析,供 path 兜底使用) const childFullPath = child.menu_path || child.path || '' + + // 优先使用子菜单自己的 component,否则继承父级的 component;再按路径兜底 + let componentPath = child.component || parentComponent + const matchedFallback = MENU_PATH_COMPONENT_FALLBACK.find((fb) => fb.test(childFullPath)) + if (matchedFallback) { + componentPath = matchedFallback.component + } + const relativePath = extractRelativePath(childFullPath, parentPath || '') const route: AppRouteRecordRaw = { diff --git a/src/router/routes/modules/ops.ts b/src/router/routes/modules/ops.ts index 0cc058c..1631cc2 100644 --- a/src/router/routes/modules/ops.ts +++ b/src/router/routes/modules/ops.ts @@ -32,6 +32,16 @@ const OPS: AppRouteRecordRaw = { roles: ['*'], }, }, + { + path: 'license-center', + name: 'LicenseCenter', + component: () => import('@/views/ops/pages/system-settings/license-center/index.vue'), + meta: { + locale: 'menu.ops.systemSettings.licenseCenter', + requiresAuth: true, + roles: ['*'], + }, + }, { path: 'web-test', name: 'WebTest', diff --git a/src/views/ops/pages/system-settings/license-center/index.vue b/src/views/ops/pages/system-settings/license-center/index.vue new file mode 100644 index 0000000..34bd380 --- /dev/null +++ b/src/views/ops/pages/system-settings/license-center/index.vue @@ -0,0 +1,379 @@ + + + + + + + From 87741c59ff865afc279d4dd21a5c0c0189efd4e6 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sat, 21 Mar 2026 17:13:47 +0800 Subject: [PATCH 2/5] fix --- src/router/menu-data.ts | 27 +++++++++++++++++---------- src/router/routes/modules/ops.ts | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/router/menu-data.ts b/src/router/menu-data.ts index 1b33029..a69c164 100644 --- a/src/router/menu-data.ts +++ b/src/router/menu-data.ts @@ -181,10 +181,16 @@ function extractRelativePath(childPath: string, parentPath: string): string { * @param parentIsFull 父级菜单的 is_full 字段 * @returns 子路由配置数组 */ -/** 服务端未配置 component 时按 menu_path 绑定视图(避免误用 redirect 导致白屏) */ -const MENU_PATH_COMPONENT_FALLBACK: { test: (fullPath: string) => boolean; component: string }[] = [ - { test: (p) => p.includes('license-center'), component: 'ops/pages/system-settings/license-center' }, -] +/** 许可授权中心菜单路径(严格匹配末段,避免 menu_path 中含 query 等导致误伤其它菜单如用户管理) */ +const LICENSE_CENTER_VIEW = 'ops/pages/system-settings/license-center' + +function isLicenseCenterMenuPath(fullPath: string): boolean { + const path = String(fullPath ?? '') + .trim() + .split('?')[0] + .replace(/\/+$/, '') + return path.endsWith('/license-center') || path === 'license-center' +} function transformChildRoutes( children: ServerMenuItem[], @@ -193,14 +199,15 @@ function transformChildRoutes( parentIsFull?: boolean ): AppRouteRecordRaw[] { return children.map((child) => { - // 计算子路由的相对路径(需先于 component 解析,供 path 兜底使用) - const childFullPath = child.menu_path || child.path || '' + const childFullPath = String(child.menu_path ?? child.path ?? '').trim() - // 优先使用子菜单自己的 component,否则继承父级的 component;再按路径兜底 + // 已配置 component 的菜单绝不覆盖;仅对许可页做路径/code 兜底,避免 includes 误匹配 let componentPath = child.component || parentComponent - const matchedFallback = MENU_PATH_COMPONENT_FALLBACK.find((fb) => fb.test(childFullPath)) - if (matchedFallback) { - componentPath = matchedFallback.component + if ( + !child.component && + (isLicenseCenterMenuPath(childFullPath) || child.code === 'LicenseCenter') + ) { + componentPath = LICENSE_CENTER_VIEW } const relativePath = extractRelativePath(childFullPath, parentPath || '') diff --git a/src/router/routes/modules/ops.ts b/src/router/routes/modules/ops.ts index 1631cc2..555e435 100644 --- a/src/router/routes/modules/ops.ts +++ b/src/router/routes/modules/ops.ts @@ -34,7 +34,7 @@ const OPS: AppRouteRecordRaw = { }, { path: 'license-center', - name: 'LicenseCenter', + name: 'OpsLicenseCenter', component: () => import('@/views/ops/pages/system-settings/license-center/index.vue'), meta: { locale: 'menu.ops.systemSettings.licenseCenter', From 6ac95501335585d66dacd5b27a55f305b85a290d Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Sat, 21 Mar 2026 17:19:33 +0800 Subject: [PATCH 3/5] fix --- .../system-settings/system-logs/index.vue | 177 +++++++++++++----- 1 file changed, 128 insertions(+), 49 deletions(-) diff --git a/src/views/ops/pages/system-settings/system-logs/index.vue b/src/views/ops/pages/system-settings/system-logs/index.vue index 96df1d5..22d5a66 100644 --- a/src/views/ops/pages/system-settings/system-logs/index.vue +++ b/src/views/ops/pages/system-settings/system-logs/index.vue @@ -1,8 +1,7 @@