修复(权限管理):统一 PM 系统导航与权限校验
This commit is contained in:
@@ -30,6 +30,17 @@
|
||||
<section class="access-log-card">
|
||||
<div class="audit-filters">
|
||||
<div class="filter-inputs">
|
||||
<el-tag
|
||||
v-if="trendDrilldownActive"
|
||||
closable
|
||||
effect="plain"
|
||||
size="small"
|
||||
type="primary"
|
||||
class="trend-drilldown-tag"
|
||||
@close="clearTrendDrilldownRange"
|
||||
>
|
||||
权限趋势下钻{{ drilldownRangeLabel ? ` · ${drilldownRangeLabel}` : "" }}
|
||||
</el-tag>
|
||||
<el-select v-model="filters.role" placeholder="角色" clearable size="small" class="filter-role" @change="onFilterChange">
|
||||
<el-option v-for="role in roleFilterOptions" :key="role.value" :label="role.label" :value="role.value" />
|
||||
</el-select>
|
||||
@@ -324,6 +335,17 @@ const TIME_RANGE_PRESETS = [
|
||||
] as const;
|
||||
|
||||
type TimeRangePreset = (typeof TIME_RANGE_PRESETS)[number]["value"];
|
||||
type AccessTimeRangePreset = TimeRangePreset | "custom";
|
||||
|
||||
interface TrendDrilldownPayload {
|
||||
preset?: Exclude<TimeRangePreset, "all">;
|
||||
startAt?: string;
|
||||
endAt?: string;
|
||||
allowed?: boolean;
|
||||
role?: string;
|
||||
clientType?: string;
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
interface IpRankingRow {
|
||||
ip: string;
|
||||
@@ -385,8 +407,10 @@ const summary = ref<AccessLogsSummary>(emptySummary);
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(50);
|
||||
const timeRangePreset = ref<TimeRangePreset>("24h");
|
||||
const timeRangePreset = ref<AccessTimeRangePreset>("24h");
|
||||
const ipRankingPeriod = ref<TimeRangePreset>("24h");
|
||||
const trendDrilldownRange = ref<[Date, Date] | null>(null);
|
||||
const trendDrilldownActive = ref(false);
|
||||
const accessLogDialogVisible = ref(false);
|
||||
const ipRankingDialogVisible = ref(false);
|
||||
const accessTerminalWindowRef = ref<HTMLElement | null>(null);
|
||||
@@ -636,7 +660,8 @@ 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 buildTimeRangeFromPreset = (preset: TimeRangePreset): [Date, Date] | null => {
|
||||
const buildTimeRangeFromPreset = (preset: AccessTimeRangePreset): [Date, Date] | null => {
|
||||
if (preset === "custom") return null;
|
||||
const option = TIME_RANGE_PRESETS.find((item) => item.value === preset) || TIME_RANGE_PRESETS[1];
|
||||
if (!option.hours) return null;
|
||||
const end = new Date();
|
||||
@@ -691,6 +716,7 @@ const onFilterChange = () => {
|
||||
};
|
||||
|
||||
const onTimePresetChange = () => {
|
||||
trendDrilldownRange.value = null;
|
||||
onFilterChange();
|
||||
};
|
||||
|
||||
@@ -704,6 +730,8 @@ const onKeywordInput = () => {
|
||||
|
||||
const resetFilters = () => {
|
||||
timeRangePreset.value = "24h";
|
||||
trendDrilldownRange.value = null;
|
||||
trendDrilldownActive.value = false;
|
||||
filters.role = undefined;
|
||||
filters.allowed = undefined;
|
||||
filters.clientType = undefined;
|
||||
@@ -724,12 +752,13 @@ const buildQueryParams = (overrides: Record<string, any> = {}) => {
|
||||
if (filters.role) params.role = filters.role;
|
||||
if (filters.allowed !== undefined) params.allowed = filters.allowed;
|
||||
if (filters.clientType) params.client_type = filters.clientType;
|
||||
if (trendDrilldownActive.value) params.event_type = "permission";
|
||||
const keywordToken = filters.keyword.trim();
|
||||
if (keywordToken) {
|
||||
params.keyword = keywordToken;
|
||||
if (/^[0-9a-f:.]+$/i.test(keywordToken)) params.client_ip = keywordToken;
|
||||
}
|
||||
const presetRange = timeRangePreset.value ? buildTimeRangeFromPreset(timeRangePreset.value) : null;
|
||||
const presetRange = trendDrilldownRange.value || buildTimeRangeFromPreset(timeRangePreset.value);
|
||||
if (presetRange) {
|
||||
params.start_time = presetRange[0].toISOString();
|
||||
params.end_time = presetRange[1].toISOString();
|
||||
@@ -796,6 +825,37 @@ const refresh = () => {
|
||||
loadData();
|
||||
};
|
||||
|
||||
const drilldownRangeLabel = computed(() => {
|
||||
if (!trendDrilldownRange.value) return "";
|
||||
const [start, end] = trendDrilldownRange.value;
|
||||
const compact = (value: Date) => `${value.getMonth() + 1}/${value.getDate()} ${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}`;
|
||||
return `${compact(start)}–${compact(end)}`;
|
||||
});
|
||||
|
||||
const clearTrendDrilldownRange = () => {
|
||||
trendDrilldownRange.value = null;
|
||||
trendDrilldownActive.value = false;
|
||||
timeRangePreset.value = "24h";
|
||||
onFilterChange();
|
||||
};
|
||||
|
||||
const applyTrendFilter = async (payload: TrendDrilldownPayload) => {
|
||||
page.value = 1;
|
||||
trendDrilldownActive.value = true;
|
||||
filters.allowed = payload.allowed;
|
||||
filters.role = payload.role;
|
||||
filters.clientType = payload.clientType;
|
||||
filters.keyword = payload.keyword || "";
|
||||
if (payload.startAt && payload.endAt) {
|
||||
trendDrilldownRange.value = [new Date(payload.startAt), new Date(payload.endAt)];
|
||||
timeRangePreset.value = "custom";
|
||||
} else {
|
||||
trendDrilldownRange.value = null;
|
||||
timeRangePreset.value = payload.preset || "24h";
|
||||
}
|
||||
await loadData();
|
||||
};
|
||||
|
||||
const openLogDetail = (row: AccessLogItem) => {
|
||||
selectedLog.value = row;
|
||||
detailDrawerVisible.value = true;
|
||||
@@ -1023,7 +1083,7 @@ onBeforeUnmount(() => {
|
||||
stopTimers();
|
||||
});
|
||||
|
||||
defineExpose({ refresh });
|
||||
defineExpose({ refresh, applyTrendFilter });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1089,12 +1149,12 @@ defineExpose({ refresh });
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 0 8px;
|
||||
padding: 10px 12px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid var(--audit-border);
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.filter-inputs {
|
||||
@@ -1249,19 +1309,14 @@ defineExpose({ refresh });
|
||||
|
||||
.access-log-card {
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--audit-border);
|
||||
border-radius: var(--card-radius);
|
||||
background: #fff;
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.access-log-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -1302,14 +1357,14 @@ defineExpose({ refresh });
|
||||
.log-surface {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.table-shell {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #edf2f7;
|
||||
border-radius: 10px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.access-table {
|
||||
@@ -1331,6 +1386,14 @@ defineExpose({ refresh });
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.access-table :deep(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.access-table :deep(.el-scrollbar__bar.is-horizontal) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.time-cell,
|
||||
.ip-cell,
|
||||
.location-cell,
|
||||
@@ -1608,7 +1671,8 @@ defineExpose({ refresh });
|
||||
.logs-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 10px;
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid var(--audit-border);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user