拆分系统监测模块并统一运维化文案
This commit is contained in:
@@ -28,6 +28,10 @@
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>{{ TEXT.menu.auditLogs }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-if="isAdmin" index="/admin/system-monitoring">
|
||||
<el-icon><DataAnalysis /></el-icon>
|
||||
<span>{{ TEXT.menu.systemMonitoring }}</span>
|
||||
</el-menu-item>
|
||||
<el-sub-menu v-if="canAccessAdminPermissions" index="permissions">
|
||||
<template #title>
|
||||
<el-icon><Key /></el-icon>
|
||||
@@ -35,7 +39,6 @@
|
||||
</template>
|
||||
<el-menu-item index="/admin/permissions/system">系统级权限</el-menu-item>
|
||||
<el-menu-item index="/admin/permissions/project">项目权限配置</el-menu-item>
|
||||
<el-menu-item index="/admin/permissions/monitoring">权限监控</el-menu-item>
|
||||
</el-sub-menu>
|
||||
</el-menu-item-group>
|
||||
|
||||
@@ -235,7 +238,7 @@ import { fetchSites } from "../api/sites";
|
||||
import { TEXT } from "../locales";
|
||||
import {
|
||||
User, Suitcase, House, Calendar, Flag,
|
||||
CircleCheck, Box, Coin, Notebook, Document, ArrowDown, SwitchButton, Files, Key
|
||||
CircleCheck, Box, Coin, Notebook, Document, ArrowDown, SwitchButton, Files, Key, DataAnalysis
|
||||
} from "@element-plus/icons-vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { getProjectRoutePermission, hasProjectPermission, projectRouteLandingPaths } from "../utils/projectRoutePermissions";
|
||||
@@ -301,6 +304,7 @@ const activeMenu = computed(() => {
|
||||
if (path.startsWith("/knowledge/instruction-files")) return "/knowledge/instruction-files";
|
||||
if (path.startsWith("/projects/")) return "/admin/projects";
|
||||
if (path.startsWith("/admin/projects/")) return "/admin/projects";
|
||||
if (path.startsWith("/admin/system-monitoring") || path.startsWith("/admin/permission-monitoring")) return "/admin/system-monitoring";
|
||||
if (path.startsWith("/admin/permissions/")) return path;
|
||||
return path;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const readSource = () => readFileSync(resolve(__dirname, "./Layout.vue"), "utf8");
|
||||
|
||||
describe("layout system monitoring navigation", () => {
|
||||
it("shows system monitoring as an admin-only peer module outside permission management", () => {
|
||||
const source = readSource();
|
||||
const permissionMenuStart = source.indexOf('<el-sub-menu v-if="canAccessAdminPermissions" index="permissions">');
|
||||
const permissionMenuEnd = source.indexOf("</el-sub-menu>", permissionMenuStart);
|
||||
const permissionMenu = source.slice(permissionMenuStart, permissionMenuEnd);
|
||||
|
||||
expect(source).toContain('v-if="isAdmin" index="/admin/system-monitoring"');
|
||||
expect(source).toContain("{{ TEXT.menu.systemMonitoring }}");
|
||||
expect(permissionMenu).not.toContain("/admin/system-monitoring");
|
||||
expect(permissionMenu).not.toContain("/admin/permission-monitoring");
|
||||
expect(permissionMenu).not.toContain("/admin/permissions/monitoring");
|
||||
expect(source).not.toContain('index="/admin/permissions/monitoring"');
|
||||
expect(source).toContain('return "/admin/system-monitoring";');
|
||||
});
|
||||
});
|
||||
@@ -21,13 +21,25 @@ describe("PermissionIpLocations", () => {
|
||||
|
||||
expect(source).toContain("VChart");
|
||||
expect(source).toContain("chinaMapOption");
|
||||
expect(source).toContain("用户分布图");
|
||||
expect(source).toContain("访问来源分布");
|
||||
expect(source).toContain("访问次数");
|
||||
expect(source).toContain("访问用户数");
|
||||
expect(source).toContain("来源 IP 数");
|
||||
expect(source).toContain("summary");
|
||||
});
|
||||
|
||||
it("uses source-analysis copy instead of IP-location tab wording", () => {
|
||||
const source = readSource();
|
||||
|
||||
expect(source).toContain("访问来源分析");
|
||||
expect(source).toContain("来源分布");
|
||||
expect(source).toContain("热点来源地");
|
||||
expect(source).not.toContain("IP 属地分析");
|
||||
expect(source).not.toContain("中国地图");
|
||||
expect(source).not.toContain("热点属地");
|
||||
expect(source).not.toContain("用户分布图");
|
||||
});
|
||||
|
||||
it("uses the standard geojson.cn China map data instead of generated rectangles", () => {
|
||||
const source = readSource();
|
||||
const mapSource = readMapSource();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<section class="ip-hero">
|
||||
<div class="ip-locations-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<span class="toolbar-title">IP 属地分析</span>
|
||||
<span class="toolbar-title">访问来源分析</span>
|
||||
<span class="toolbar-desc">查看访问来源的地理分布</span>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
@@ -37,7 +37,7 @@
|
||||
<div class="map-panel">
|
||||
<div class="section-head">
|
||||
<div class="section-title-group">
|
||||
<h4>中国地图</h4>
|
||||
<h4>来源分布</h4>
|
||||
<p>访问来源地理分布热力图</p>
|
||||
</div>
|
||||
<el-tag type="info" effect="plain" round size="small">{{ currentPeriodLabel }}</el-tag>
|
||||
@@ -48,7 +48,7 @@
|
||||
<aside class="rank-panel">
|
||||
<div class="section-head compact">
|
||||
<div class="section-title-group">
|
||||
<h4>热点属地</h4>
|
||||
<h4>热点来源地</h4>
|
||||
<p>按访问次数排序</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -219,7 +219,7 @@ const chinaMapOption = computed(() => ({
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "用户分布图",
|
||||
name: "访问来源分布",
|
||||
type: "map",
|
||||
map: "ctms-china",
|
||||
roam: false,
|
||||
|
||||
@@ -77,4 +77,28 @@ describe("PermissionMonitoring.vue", () => {
|
||||
expect(source).toContain("isAdmin?: boolean");
|
||||
expect(source).toContain(':show-security-log="props.isAdmin"');
|
||||
});
|
||||
|
||||
it("uses system monitoring tab names and operation-oriented overview copy", () => {
|
||||
const source = readFileSync(resolve(__dirname, "./PermissionMonitoring.vue"), "utf8");
|
||||
|
||||
expect(source).toContain('label="运行概览"');
|
||||
expect(source).toContain('label="性能趋势"');
|
||||
expect(source).toContain('label="访问审计"');
|
||||
expect(source).toContain('label="来源分析"');
|
||||
expect(source).toContain('label: "今日监测事件"');
|
||||
expect(source).toContain('label: "每分钟事件"');
|
||||
expect(source).toContain('label: "今日通过率"');
|
||||
expect(source).toContain('label: "历史事件总数"');
|
||||
expect(source).toContain("异常拒绝排行");
|
||||
|
||||
expect(source).not.toContain('label="实时概览"');
|
||||
expect(source).not.toContain('label="趋势分析"');
|
||||
expect(source).not.toContain('label="访问日志"');
|
||||
expect(source).not.toContain('label="IP属地"');
|
||||
expect(source).not.toContain('label: "今日总检查"');
|
||||
expect(source).not.toContain('label: "每分钟请求"');
|
||||
expect(source).not.toContain('label: "今日允许率"');
|
||||
expect(source).not.toContain('label: "历史总记录"');
|
||||
expect(source).not.toContain("被拒绝最多的权限");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="permission-monitoring">
|
||||
<el-tabs v-model="activeTab" @tab-change="onTabChange">
|
||||
<el-tab-pane label="实时概览" name="overview">
|
||||
<el-tab-pane label="运行概览" name="overview">
|
||||
<div v-if="statsSummary" class="stats-summary">
|
||||
<div v-for="card in summaryCards" :key="card.label" class="summary-card" :class="card.tone">
|
||||
<div class="summary-icon">
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<div v-if="topDenied && topDenied.length > 0" class="denied-card">
|
||||
<div class="card-header">
|
||||
<h3>被拒绝最多的权限</h3>
|
||||
<h3>异常拒绝排行</h3>
|
||||
<el-tag effect="plain" size="small" type="info">近 7 天</el-tag>
|
||||
</div>
|
||||
<div class="denied-list">
|
||||
@@ -121,15 +121,15 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="趋势分析" name="trends">
|
||||
<el-tab-pane label="性能趋势" name="trends">
|
||||
<PermissionTrendCharts ref="trendChartsRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="访问日志" name="logs">
|
||||
<el-tab-pane label="访问审计" name="logs">
|
||||
<PermissionAccessLogs ref="accessLogsRef" :show-security-log="props.isAdmin" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="IP属地" name="ip-locations">
|
||||
<el-tab-pane label="来源分析" name="ip-locations">
|
||||
<PermissionIpLocations ref="ipLocationsRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -226,19 +226,19 @@ const summaryCards = computed(() => {
|
||||
if (!statsSummary.value) return [];
|
||||
return [
|
||||
{
|
||||
label: "今日总检查",
|
||||
label: "今日监测事件",
|
||||
value: statsSummary.value.today.total_checks.toLocaleString(),
|
||||
tone: "blue",
|
||||
icon: IconCheck,
|
||||
},
|
||||
{
|
||||
label: "每分钟请求",
|
||||
label: "每分钟事件",
|
||||
value: statsSummary.value.last_hour.requests_per_minute,
|
||||
tone: "cyan",
|
||||
icon: IconActivity,
|
||||
},
|
||||
{
|
||||
label: "今日允许率",
|
||||
label: "今日通过率",
|
||||
value: `${statsSummary.value.today.allow_rate}%`,
|
||||
tone: statsSummary.value.today.allow_rate >= 80 ? "green" : "warning",
|
||||
icon: IconPercent,
|
||||
@@ -250,7 +250,7 @@ const summaryCards = computed(() => {
|
||||
icon: IconClock,
|
||||
},
|
||||
{
|
||||
label: "历史总记录",
|
||||
label: "历史事件总数",
|
||||
value: statsSummary.value.total_logs.toLocaleString(),
|
||||
tone: "violet",
|
||||
icon: IconDatabase,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const readSource = () => readFileSync(resolve(__dirname, "./PermissionTrendCharts.vue"), "utf8");
|
||||
|
||||
describe("PermissionTrendCharts", () => {
|
||||
it("uses operation-oriented trend copy", () => {
|
||||
const source = readSource();
|
||||
|
||||
expect(source).toContain("系统性能趋势");
|
||||
expect(source).toContain("查看系统运行监测指标的变化趋势");
|
||||
expect(source).toContain("监测事件趋势");
|
||||
expect(source).toContain("响应时间趋势");
|
||||
expect(source).toContain("缓存命中率");
|
||||
expect(source).toContain("异常拒绝率趋势");
|
||||
expect(source).toContain('name: "异常拒绝率"');
|
||||
|
||||
expect(source).not.toContain("数据趋势");
|
||||
expect(source).not.toContain("查看权限系统各项指标的变化趋势");
|
||||
expect(source).not.toContain("检查量趋势");
|
||||
expect(source).not.toContain("<h4>拒绝率趋势</h4>");
|
||||
expect(source).not.toContain('name: "拒绝率"');
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="trend-charts">
|
||||
<div class="trend-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<span class="toolbar-title">数据趋势</span>
|
||||
<span class="toolbar-desc">查看权限系统各项指标的变化趋势</span>
|
||||
<span class="toolbar-title">系统性能趋势</span>
|
||||
<span class="toolbar-desc">查看系统运行监测指标的变化趋势</span>
|
||||
</div>
|
||||
<el-radio-group v-model="period" size="small" @change="loadData">
|
||||
<el-radio-button value="24h">24小时</el-radio-button>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="chart-icon blue">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
|
||||
</div>
|
||||
<h4>检查量趋势</h4>
|
||||
<h4>监测事件趋势</h4>
|
||||
</div>
|
||||
<v-chart :option="checksChartOption" autoresize class="chart" />
|
||||
</div>
|
||||
@@ -45,7 +45,7 @@
|
||||
<div class="chart-icon danger">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
|
||||
</div>
|
||||
<h4>拒绝率趋势</h4>
|
||||
<h4>异常拒绝率趋势</h4>
|
||||
</div>
|
||||
<v-chart :option="denyRateOption" autoresize class="chart" />
|
||||
</div>
|
||||
@@ -181,7 +181,7 @@ const denyRateOption = computed(() => ({
|
||||
yAxis: { type: "value", max: 100, name: "%", splitLine: { lineStyle: { color: "#f1f5f9" } }, axisLabel: { color: "#64748b" } },
|
||||
series: [
|
||||
{
|
||||
name: "拒绝率",
|
||||
name: "异常拒绝率",
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "circle",
|
||||
|
||||
Reference in New Issue
Block a user