fix: 审计事件详情
This commit is contained in:
@@ -3,15 +3,19 @@
|
||||
<ElButton v-if="eventId" link type="primary" @click="openEvent">事件详情</ElButton>
|
||||
<ElButton v-if="actorRef" link type="primary" @click="openActor"> 操作者行为时间线 </ElButton>
|
||||
<ElButton
|
||||
v-show="showResources"
|
||||
v-for="resource in stableResourceRefs"
|
||||
:key="`${resource.resource_type}:${resource.resource_id}`"
|
||||
link
|
||||
type="primary"
|
||||
:title="resourceTimelineLabel(resource)"
|
||||
:aria-label="resourceTimelineLabel(resource)"
|
||||
@click="openResource(resource)"
|
||||
>
|
||||
资源审计时间线
|
||||
{{ resourceTimelineLabel(resource) }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-show="showResources"
|
||||
v-for="resource in searchableResourceRefs"
|
||||
:key="`${resource.resource_type}:${resource.resource_key}`"
|
||||
link
|
||||
@@ -52,6 +56,7 @@
|
||||
AuditSearchResourceType
|
||||
} from '@/types/api'
|
||||
import { openAuditInvestigation, openAuditResourceSearch } from './investigationController'
|
||||
import { auditResourceTypeLabels } from '@/utils/business/audit'
|
||||
import { resolveInvestigationReference } from '@/utils/business/auditNavigation'
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -59,8 +64,9 @@
|
||||
refs?: AuditInvestigationRefs | null
|
||||
currentEventId?: string
|
||||
showEmpty?: boolean
|
||||
showResources?: boolean
|
||||
}>(),
|
||||
{ currentEventId: '', showEmpty: true }
|
||||
{ currentEventId: '', showEmpty: true, showResources: true }
|
||||
)
|
||||
const searchableTypes = new Set<AuditSearchResourceType>([
|
||||
'iot_card',
|
||||
@@ -111,8 +117,8 @@
|
||||
actorRef.value ||
|
||||
requestId.value ||
|
||||
correlationId.value ||
|
||||
stableResourceRefs.value.length ||
|
||||
searchableResourceRefs.value.length ||
|
||||
(props.showResources &&
|
||||
(stableResourceRefs.value.length || searchableResourceRefs.value.length)) ||
|
||||
stableIntegrationRefs.value.length
|
||||
)
|
||||
)
|
||||
@@ -135,6 +141,10 @@
|
||||
resourceType: resource.resource_type,
|
||||
id: resource.resource_id
|
||||
})
|
||||
const resourceTimelineLabel = (
|
||||
resource: AuditInvestigationResourceRef & { resource_id: string }
|
||||
) =>
|
||||
`资源审计时间线 · ${auditResourceTypeLabels[resource.resource_type] || resource.resource_type} · ${resource.resource_id}`
|
||||
const searchResource = (
|
||||
resource: AuditInvestigationResourceRef & {
|
||||
resource_type: AuditSearchResourceType
|
||||
|
||||
2
src/types/components.d.ts
vendored
2
src/types/components.d.ts
vendored
@@ -99,6 +99,8 @@ declare module 'vue' {
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
|
||||
@@ -99,32 +99,11 @@
|
||||
><AuditInvestigationLinks
|
||||
:refs="detail.investigation_refs"
|
||||
:current-event-id="detail.event_id"
|
||||
:show-resources="false"
|
||||
/></ElCard>
|
||||
<ElCard shadow="never" class="section-card">
|
||||
<template #header>相关资源与快照</template>
|
||||
<ElTable :data="detail.resources || []" :row-key="resourceRowKey">
|
||||
<ElTableColumn type="expand" width="48">
|
||||
<template #default="{ row }">
|
||||
<div class="resource-snapshot-grid">
|
||||
<section>
|
||||
<h4>身份快照</h4>
|
||||
<pre>{{ auditJson(row.identity_snapshot) }}</pre>
|
||||
</section>
|
||||
<section>
|
||||
<h4>变更前</h4>
|
||||
<pre>{{ auditJson(row.before_data) }}</pre>
|
||||
</section>
|
||||
<section>
|
||||
<h4>变更后</h4>
|
||||
<pre>{{ auditJson(row.after_data) }}</pre>
|
||||
</section>
|
||||
<section>
|
||||
<h4>主体安全数据</h4>
|
||||
<pre>{{ auditJson(row.subject_data) }}</pre>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
prop="display_name"
|
||||
label="资源"
|
||||
@@ -141,7 +120,7 @@
|
||||
auditResourceRelationLabels[row.relation] || row.relation
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="role" label="业务角色" min-width="150" />
|
||||
<ElTableColumn prop="role" label="业务角色" min-width="160" show-overflow-tooltip />
|
||||
<ElTableColumn label="主体可见性" min-width="150">
|
||||
<template #default="{ row }">{{
|
||||
auditSubjectVisibilityLabels[row.subject_visibility] ||
|
||||
@@ -160,6 +139,19 @@
|
||||
row.created_at ? formatDateTime(row.created_at) : '-'
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="220" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<ElButton link type="primary" @click="openSnapshotDialog(row)">查看快照</ElButton>
|
||||
<ElButton
|
||||
link
|
||||
type="primary"
|
||||
:disabled="!row.resource_id"
|
||||
@click="openResourceTimeline(row)"
|
||||
>
|
||||
资源审计时间线
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</ElCard>
|
||||
<ElCard v-if="userStore.isSuperAdmin" shadow="never">
|
||||
@@ -170,6 +162,37 @@
|
||||
<ElEmpty v-else-if="!loading" description="未找到审计事件" />
|
||||
</div>
|
||||
</ElCard>
|
||||
<ElDialog
|
||||
v-model="snapshotDialogVisible"
|
||||
:title="snapshotDialogTitle"
|
||||
width="min(960px, calc(100% - 32px))"
|
||||
destroy-on-close
|
||||
>
|
||||
<div v-if="selectedResource" class="resource-snapshot-grid">
|
||||
<ElCollapse class="resource-snapshot-section">
|
||||
<ElCollapseItem title="身份快照" name="identity">
|
||||
<pre>{{ auditJson(selectedResource.identity_snapshot) }}</pre>
|
||||
</ElCollapseItem>
|
||||
</ElCollapse>
|
||||
<ElCollapse class="resource-snapshot-section">
|
||||
<ElCollapseItem title="主体安全数据" name="subject">
|
||||
<pre>{{ auditJson(selectedResource.subject_data) }}</pre>
|
||||
</ElCollapseItem>
|
||||
</ElCollapse>
|
||||
<section class="resource-snapshot-diff">
|
||||
<ElCollapse class="resource-snapshot-section">
|
||||
<ElCollapseItem title="变更前" name="before">
|
||||
<pre>{{ auditJson(selectedResource.before_data) }}</pre>
|
||||
</ElCollapseItem>
|
||||
</ElCollapse>
|
||||
<ElCollapse class="resource-snapshot-section">
|
||||
<ElCollapseItem title="变更后" name="after">
|
||||
<pre>{{ auditJson(selectedResource.after_data) }}</pre>
|
||||
</ElCollapseItem>
|
||||
</ElCollapse>
|
||||
</section>
|
||||
</div>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -195,6 +218,7 @@
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import AuditInvestigationLinks from '@/components/business/audit/AuditInvestigationLinks.vue'
|
||||
import { openAuditInvestigation } from '@/components/business/audit/investigationController'
|
||||
|
||||
defineOptions({ name: 'AuditEventDetail' })
|
||||
const route = useRoute()
|
||||
@@ -202,6 +226,14 @@
|
||||
const userStore = useUserStore()
|
||||
const loading = ref(false)
|
||||
const detail = ref<AuditEventDetail>()
|
||||
const snapshotDialogVisible = ref(false)
|
||||
const selectedResource = ref<AuditResourceView>()
|
||||
const snapshotDialogTitle = computed(() => {
|
||||
const resource = selectedResource.value
|
||||
if (!resource) return '资源快照'
|
||||
const type = auditResourceTypeLabels[resource.resource_type] || resource.resource_type
|
||||
return `资源快照 · ${resource.display_name || resource.resource_id || type}`
|
||||
})
|
||||
const scopeDisplay = computed(() => {
|
||||
if (!detail.value) return '-'
|
||||
const type = auditScopeTypeLabels[detail.value.scope_type] || detail.value.scope_type
|
||||
@@ -223,6 +255,18 @@
|
||||
[row.resource_type, row.resource_id || row.resource_key || row.sort_order || row.display_name]
|
||||
.filter((item) => item !== null && item !== undefined && item !== '')
|
||||
.join(':')
|
||||
const openSnapshotDialog = (row: AuditResourceView) => {
|
||||
selectedResource.value = row
|
||||
snapshotDialogVisible.value = true
|
||||
}
|
||||
const openResourceTimeline = (row: AuditResourceView) => {
|
||||
if (!row.resource_id) return
|
||||
openAuditInvestigation({
|
||||
mode: 'resource',
|
||||
resourceType: row.resource_type,
|
||||
id: row.resource_id
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
const id = String(route.params.eventId || '')
|
||||
if (!id) return
|
||||
@@ -279,14 +323,22 @@
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
padding: 8px 16px 16px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.resource-snapshot-grid h4 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
.resource-snapshot-diff {
|
||||
display: grid;
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.resource-snapshot-section {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.resource-snapshot-section :deep(.el-collapse-item__content) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.resource-snapshot-grid pre {
|
||||
@@ -319,5 +371,9 @@
|
||||
.resource-snapshot-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.resource-snapshot-diff {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user