增加日志查询页面。

This commit is contained in:
aaron 2025-01-13 19:15:54 +08:00
parent cc8b00446c
commit 1b65954943
3 changed files with 365 additions and 1 deletions

View File

@ -58,6 +58,16 @@
<router-link to="/merchant/products">商品列表</router-link>
</a-menu-item>
</a-sub-menu>
<a-sub-menu key="system">
<template #icon>
<setting-outlined />
</template>
<template #title>系统管理</template>
<a-menu-item key="system-logs">
<router-link to="/system/logs">日志查询</router-link>
</a-menu-item>
</a-sub-menu>
</a-menu>
</a-layout-sider>
<a-layout>
@ -109,6 +119,7 @@ import {
LogoutOutlined,
DashboardOutlined,
ShopOutlined,
SettingOutlined,
} from '@ant-design/icons-vue'
import { useRouter } from 'vue-router'
@ -123,12 +134,13 @@ export default defineComponent({
LogoutOutlined,
DashboardOutlined,
ShopOutlined,
SettingOutlined,
},
setup() {
const router = useRouter()
const collapsed = ref(false)
const selectedKeys = ref(['dashboard'])
const openKeys = ref(['user', 'community', 'merchant'])
const openKeys = ref(['user', 'community', 'merchant', 'system'])
const userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || '{}'))

View File

@ -67,6 +67,17 @@ const routes = [
meta: { title: '商品列表' }
}
]
},
{
path: '/system',
component: BasicLayout,
children: [
{
path: 'logs',
component: () => import('@/views/system/LogList.vue'),
meta: { title: '日志查询' }
}
]
}
]

View File

@ -0,0 +1,341 @@
<template>
<page-container>
<div class="log-list">
<div class="table-header">
<h1>日志查询</h1>
</div>
<!-- 过滤区域 -->
<div class="table-filter">
<a-form layout="inline">
<a-form-item label="日期范围">
<a-select
v-model:value="filterForm.dateType"
style="width: 200px"
@change="handleDateTypeChange"
>
<a-select-option value="">全部</a-select-option>
<a-select-option value="today">今天</a-select-option>
<a-select-option value="yesterday">昨天</a-select-option>
<a-select-option value="last7days">过去7天</a-select-option>
<a-select-option value="last30days">过去30天</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="用户ID">
<a-input-number
v-model:value="filterForm.user_id"
style="width: 160px"
placeholder="请输入用户ID"
:min="1"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="loading" @click="handleSearch">
查询
</a-button>
<a-button style="margin-left: 8px" @click="handleReset">
重置
</a-button>
</a-form-item>
</a-form>
</div>
<a-table
:columns="columns"
:data-source="tableData"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
row-key="id"
:scroll="{ x: 1200 }"
>
<template #bodyCell="{ column, text, record }">
<!-- 时间列 -->
<template v-if="column.key === 'create_time'">
{{ formatDateTime(text) }}
</template>
<!-- 状态码列 -->
<template v-if="column.key === 'status_code'">
<a-tag :color="text >= 400 ? 'red' : (text >= 300 ? 'orange' : 'green')">
{{ text }}
</a-tag>
</template>
<!-- 请求方法列 -->
<template v-if="column.key === 'method'">
<a-tag :color="methodColors[text] || 'default'">
{{ text }}
</a-tag>
</template>
<!-- 请求路径列 -->
<template v-if="column.key === 'path'">
<a-tooltip :title="text">
{{ text }}
</a-tooltip>
</template>
</template>
</a-table>
</div>
</page-container>
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
import { message, DatePicker, Tag, InputNumber, Select } from 'ant-design-vue'
import dayjs from 'dayjs'
import PageContainer from '@/components/PageContainer.vue'
import request from '@/utils/request'
export default defineComponent({
components: {
PageContainer,
ATag: Tag,
AInputNumber: InputNumber,
ASelect: Select,
ASelectOption: Select.Option
},
setup() {
const loading = ref(false)
const tableData = ref([])
const filterForm = ref({
dateType: '',
user_id: undefined,
start_time: undefined,
end_time: undefined
})
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total} 条记录`
})
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center'
},
{
title: '用户ID',
dataIndex: 'user_id',
key: 'user_id',
width: 100,
align: 'center'
},
{
title: '请求路径',
dataIndex: 'path',
key: 'path',
width: 300,
ellipsis: true
},
{
title: '请求方法',
dataIndex: 'method',
key: 'method',
width: 100,
align: 'center'
},
{
title: '状态码',
dataIndex: 'status_code',
key: 'status_code',
width: 100,
align: 'center'
},
{
title: 'IP地址',
dataIndex: 'ip_address',
key: 'ip_address',
width: 140,
align: 'center'
},
{
title: '响应时间(ms)',
dataIndex: 'response_time',
key: 'response_time',
width: 120,
align: 'center'
},
{
title: '请求时间',
dataIndex: 'create_time',
key: 'create_time',
width: 180,
align: 'center'
}
]
const methodColors = {
GET: 'blue',
POST: 'green',
PUT: 'orange',
DELETE: 'red'
}
//
const fetchData = async () => {
try {
loading.value = true
const params = {
user_id: filterForm.value.user_id
}
if (filterForm.value.start_time) {
params.start_time = filterForm.value.start_time
}
if (filterForm.value.end_time) {
params.end_time = filterForm.value.end_time
}
const res = await request.get('/api/logs/request-logs', { params })
if (res.code === 200 && res.data?.items) {
tableData.value = res.data.items
pagination.value.total = res.data.total
} else {
tableData.value = []
pagination.value.total = 0
}
} catch (error) {
console.error('获取日志列表失败:', error)
message.error('获取日志列表失败')
tableData.value = []
pagination.value.total = 0
} finally {
loading.value = false
}
}
//
const handleDateTypeChange = (value) => {
const now = dayjs()
let start, end
switch (value) {
case 'today':
start = now.startOf('day')
end = now.endOf('day')
break
case 'yesterday':
start = now.subtract(1, 'day').startOf('day')
end = now.subtract(1, 'day').endOf('day')
break
case 'last7days':
start = now.subtract(6, 'day').startOf('day')
end = now.endOf('day')
break
case 'last30days':
start = now.subtract(29, 'day').startOf('day')
end = now.endOf('day')
break
default:
start = undefined
end = undefined
}
filterForm.value.start_time = start ? start.format('YYYY-MM-DDTHH:mm:ss') : undefined
filterForm.value.end_time = end ? end.format('YYYY-MM-DDTHH:mm:ss') : undefined
//
handleSearch()
}
//
const handleSearch = () => {
pagination.value.current = 1
fetchData()
}
//
const handleReset = () => {
filterForm.value = {
dateType: '',
user_id: undefined,
start_time: undefined,
end_time: undefined
}
pagination.value.current = 1
fetchData()
}
onMounted(() => {
fetchData()
})
//
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchData()
}
//
const formatDateTime = (dateStr) => {
if (!dateStr) return ''
return dayjs(dateStr).format('YYYY-MM-DD HH:mm:ss')
}
return {
loading,
columns,
tableData,
pagination,
filterForm,
handleDateTypeChange,
handleSearch,
handleReset,
handleTableChange,
formatDateTime,
methodColors
}
}
})
</script>
<style scoped>
.log-list {
padding: 24px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.table-header h1 {
margin: 0;
}
.table-filter {
margin-bottom: 16px;
padding: 16px 24px;
background: #fff;
border-radius: 2px;
}
:deep(.ant-form-item) {
margin-bottom: 0;
margin-right: 16px;
}
:deep(.ant-table-cell) {
white-space: nowrap;
}
:deep(.ant-table-body) {
overflow-x: auto !important;
}
:deep(.ant-select) {
width: 200px !important;
}
</style>