176 lines
6.6 KiB
Vue
176 lines
6.6 KiB
Vue
<template>
|
||
<div class="data-perm-audit-container">
|
||
<div class="data-perm-audit-card">
|
||
<a-tabs v-model:active-key="activeTab">
|
||
<a-tab-pane key="change" title="配置变更日志">
|
||
<div class="table-toolbar">
|
||
<a-space wrap>
|
||
<a-select v-model="changeQuery.targetType" placeholder="目标类型" allow-clear style="width: 120px">
|
||
<a-option value="user">用户</a-option>
|
||
<a-option value="role">角色</a-option>
|
||
<a-option value="template">模板</a-option>
|
||
</a-select>
|
||
<a-input v-model="changeQuery.targetId" placeholder="目标 ID" allow-clear style="width: 200px" />
|
||
<a-range-picker v-model="changeQuery.timeRange" show-time style="width: 360px" />
|
||
<a-button class="app-btn-search" @click="loadChangeLog">{{ t('common.search') }}</a-button>
|
||
</a-space>
|
||
<PaginationPro
|
||
:total-count="changePagination.total"
|
||
v-model:current-page="changePagination.current"
|
||
v-model:page-size="changePagination.pageSize"
|
||
@page-change="(p) => { changePagination.current = p; loadChangeLog() }"
|
||
@page-size-change="(s) => { changePagination.pageSize = s; changePagination.current = 1; loadChangeLog() }"
|
||
/>
|
||
</div>
|
||
<a-table :data="changeLogs" :loading="changeLoading" :pagination="false" row-key="id" size="small">
|
||
<template #columns>
|
||
<a-table-column title="时间" data-index="createTime" :width="180" />
|
||
<a-table-column title="目标" :width="100">
|
||
<template #cell="{ record }">{{ record.targetType }}</template>
|
||
</a-table-column>
|
||
<a-table-column title="目标 ID" data-index="targetId" ellipsis />
|
||
<a-table-column title="动作" data-index="action" :width="100" />
|
||
<a-table-column title="操作人 mapId" data-index="operatorMapId" :width="110" />
|
||
</template>
|
||
</a-table>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="access" title="数据访问日志">
|
||
<a-alert type="info" style="margin-bottom: 12px">
|
||
P1 后端暂不自动写入访问日志;本页为预留查询界面,待内审需求启用后再对接。
|
||
</a-alert>
|
||
<div class="table-toolbar">
|
||
<a-space wrap>
|
||
<a-input v-model="accessQuery.userId" placeholder="用户 GUID" allow-clear style="width: 220px" />
|
||
<a-select
|
||
v-model="accessQuery.dataResourceCode"
|
||
placeholder="模块"
|
||
allow-clear
|
||
style="width: 140px"
|
||
:options="[
|
||
{ value: 'Order', label: '订单' },
|
||
{ value: 'Customer', label: '客户' },
|
||
]"
|
||
/>
|
||
<a-range-picker v-model="accessQuery.timeRange" show-time style="width: 360px" />
|
||
<a-button class="app-btn-search" @click="loadAccessLog">{{ t('common.search') }}</a-button>
|
||
</a-space>
|
||
<PaginationPro
|
||
:total-count="accessPagination.total"
|
||
v-model:current-page="accessPagination.current"
|
||
v-model:page-size="accessPagination.pageSize"
|
||
@page-change="(p) => { accessPagination.current = p; loadAccessLog() }"
|
||
@page-size-change="(s) => { accessPagination.pageSize = s; accessPagination.current = 1; loadAccessLog() }"
|
||
/>
|
||
</div>
|
||
<a-table :data="accessLogs" :loading="accessLoading" :pagination="false" row-key="id" size="small">
|
||
<template #columns>
|
||
<a-table-column title="时间" data-index="createTime" :width="180" />
|
||
<a-table-column title="模块" data-index="dataResourceCode" :width="100" />
|
||
<a-table-column title="接口" data-index="apiPath" ellipsis />
|
||
<a-table-column title="行数" data-index="rowCount" :width="80" align="center" />
|
||
</template>
|
||
</a-table>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { reactive, ref } from 'vue'
|
||
import { useI18n } from 'vue-i18n'
|
||
import PaginationPro from '@/components/PaginationPro/index.vue'
|
||
import { getAccessLogList, getChangeLogList, type AccessLogItem, type ChangeLogItem } from '@/api/rbac/data-perm'
|
||
import { handleApiError } from '@/utils/rbac-error-handler'
|
||
|
||
const { t } = useI18n()
|
||
const activeTab = ref('change')
|
||
|
||
const changeLoading = ref(false)
|
||
const changeLogs = ref<ChangeLogItem[]>([])
|
||
const changeQuery = reactive({
|
||
targetType: '',
|
||
targetId: '',
|
||
timeRange: [] as string[],
|
||
})
|
||
const changePagination = reactive({ current: 1, pageSize: 30, total: 0 })
|
||
|
||
const accessLoading = ref(false)
|
||
const accessLogs = ref<AccessLogItem[]>([])
|
||
const accessQuery = reactive({
|
||
userId: '',
|
||
dataResourceCode: '',
|
||
timeRange: [] as string[],
|
||
})
|
||
const accessPagination = reactive({ current: 1, pageSize: 30, total: 0 })
|
||
|
||
function buildTimeParams(range: string[]) {
|
||
if (!range?.length || range.length < 2) return {}
|
||
return { startTime: range[0], endTime: range[1] }
|
||
}
|
||
|
||
async function loadChangeLog() {
|
||
changeLoading.value = true
|
||
try {
|
||
const res = await getChangeLogList({
|
||
targetType: changeQuery.targetType || undefined,
|
||
targetId: changeQuery.targetId || undefined,
|
||
page: changePagination.current,
|
||
pageSize: changePagination.pageSize,
|
||
...buildTimeParams(changeQuery.timeRange),
|
||
})
|
||
changeLogs.value = res.items || []
|
||
changePagination.total = res.total || 0
|
||
} catch (e) {
|
||
changeLogs.value = []
|
||
handleApiError(e)
|
||
} finally {
|
||
changeLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function loadAccessLog() {
|
||
accessLoading.value = true
|
||
try {
|
||
const res = await getAccessLogList({
|
||
userId: accessQuery.userId || undefined,
|
||
dataResourceCode: accessQuery.dataResourceCode || undefined,
|
||
page: accessPagination.current,
|
||
pageSize: accessPagination.pageSize,
|
||
...buildTimeParams(accessQuery.timeRange),
|
||
})
|
||
accessLogs.value = res.items || []
|
||
accessPagination.total = res.total || 0
|
||
} catch (e) {
|
||
accessLogs.value = []
|
||
handleApiError(e)
|
||
} finally {
|
||
accessLoading.value = false
|
||
}
|
||
}
|
||
|
||
loadChangeLog()
|
||
</script>
|
||
|
||
<style scoped>
|
||
.data-perm-audit-container {
|
||
padding: 16px;
|
||
background: var(--app-page-bg);
|
||
flex: 1;
|
||
}
|
||
.data-perm-audit-card {
|
||
background: var(--app-card-bg);
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
}
|
||
.table-toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 16px;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
</style>
|