142 lines
3.9 KiB
Vue
142 lines
3.9 KiB
Vue
<template>
|
|
<div class="page-shell">
|
|
<section class="page-card table-fill">
|
|
<div class="page-toolbar">
|
|
<n-form inline :show-label="false" class="toolbar-form">
|
|
<n-form-item>
|
|
<n-input v-model:value="query.keyword" clearable placeholder="用户/路径" />
|
|
</n-form-item>
|
|
<n-form-item>
|
|
<n-select
|
|
v-model:value="query.status"
|
|
clearable
|
|
placeholder="状态"
|
|
:options="statusOptions"
|
|
style="width: 140px"
|
|
/>
|
|
</n-form-item>
|
|
<n-form-item>
|
|
<n-space>
|
|
<n-button type="primary" @click="refetch">查询</n-button>
|
|
<n-button @click="reset">重置</n-button>
|
|
</n-space>
|
|
</n-form-item>
|
|
</n-form>
|
|
</div>
|
|
<div class="card-body card-body-fill table-fill">
|
|
<n-data-table
|
|
remote
|
|
size="small"
|
|
:columns="columns"
|
|
:data="rows"
|
|
:loading="operationLogQuery.isLoading.value"
|
|
:pagination="pagination"
|
|
:row-key="(row: OperationLogItem) => row.id"
|
|
@update:page="onPage"
|
|
@update:page-size="onPageSize"
|
|
/>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, h, reactive } from 'vue'
|
|
import { NTag, type DataTableColumns } from 'naive-ui'
|
|
import { useQuery } from '@tanstack/vue-query'
|
|
import { listOperationLogsApi } from '@/api/logs'
|
|
import type { OperationLogItem } from '@/types/logs'
|
|
import { httpMethodLabel, operationTypeLabel, statusLabel, statusTagType } from '@/utils/display'
|
|
import { renderPagePrefix } from '@/utils/pagination'
|
|
|
|
const query = reactive({
|
|
page: 1,
|
|
pageSize: 20,
|
|
keyword: '',
|
|
status: null as string | null
|
|
})
|
|
|
|
const statusOptions = [
|
|
{ label: '成功', value: 'SUCCESS' },
|
|
{ label: '失败', value: 'FAIL' }
|
|
]
|
|
|
|
const operationLogQuery = useQuery({
|
|
queryKey: computed(() => ['logs', 'operation', { ...query }]),
|
|
queryFn: () =>
|
|
listOperationLogsApi({
|
|
page: query.page,
|
|
pageSize: query.pageSize,
|
|
keyword: query.keyword || undefined,
|
|
status: query.status || undefined
|
|
})
|
|
})
|
|
|
|
const rows = computed(() => operationLogQuery.data.value?.items ?? [])
|
|
|
|
const pagination = computed(() => ({
|
|
page: query.page,
|
|
pageSize: query.pageSize,
|
|
itemCount: operationLogQuery.data.value?.total ?? 0,
|
|
showSizePicker: true,
|
|
pageSizes: [10, 20, 50],
|
|
prefix: renderPagePrefix
|
|
}))
|
|
|
|
function refetch() {
|
|
query.page = 1
|
|
operationLogQuery.refetch()
|
|
}
|
|
|
|
function reset() {
|
|
query.keyword = ''
|
|
query.status = null
|
|
query.page = 1
|
|
operationLogQuery.refetch()
|
|
}
|
|
|
|
function onPage(page: number) {
|
|
query.page = page
|
|
}
|
|
|
|
function onPageSize(pageSize: number) {
|
|
query.pageSize = pageSize
|
|
query.page = 1
|
|
}
|
|
|
|
const columns = computed<DataTableColumns<OperationLogItem>>(() => [
|
|
{ title: '时间', key: 'createdAt', width: 170 },
|
|
{ title: '用户', key: 'username', minWidth: 110, render: (row) => row.username || '-' },
|
|
{
|
|
title: '操作',
|
|
key: 'operationName',
|
|
minWidth: 180,
|
|
render: (row) =>
|
|
h('div', { class: 'leading-5' }, [
|
|
h('div', { class: 'font-medium text-slate-900' }, row.operationName),
|
|
h('div', { class: 'text-xs text-slate-500' }, operationTypeLabel(row.operationType))
|
|
])
|
|
},
|
|
{ title: '方法', key: 'httpMethod', width: 90, render: (row) => httpMethodLabel(row.httpMethod) },
|
|
{ title: '路径', key: 'requestPath', minWidth: 220 },
|
|
{
|
|
title: '状态',
|
|
key: 'status',
|
|
width: 90,
|
|
render: (row) =>
|
|
h(
|
|
NTag,
|
|
{ type: statusTagType(row.status), size: 'small' },
|
|
{ default: () => statusLabel(row.status) }
|
|
)
|
|
},
|
|
{ title: '耗时', key: 'costMs', width: 90, render: (row) => `${row.costMs} ms` },
|
|
{
|
|
title: '错误信息',
|
|
key: 'errorMessage',
|
|
minWidth: 180,
|
|
render: (row) => row.errorMessage || '-'
|
|
}
|
|
])
|
|
</script>
|