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] 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 @@ + + + + + + + + 许可证信息 + {{ license?.company_name || '—' }} + + + + + + 刷新 + + + + + + + + + + 基本信息 + + + + {{ row.label }} + {{ row.display }} + + + + + + + 资源限制 + + 配额项为 0 时表示该维度不按许可证限制数量(由业务逻辑决定)。 + + + {{ row.label }} + {{ row.display }} + + + + + + + + + + + + + +
配额项为 0 时表示该维度不按许可证限制数量(由业务逻辑决定)。