Compare commits
3 Commits
7a5dde79ac
...
ecf4129dfc
| Author | SHA1 | Date |
|---|---|---|
|
|
ecf4129dfc | 1 month ago |
|
|
ce39d8e83c | 1 month ago |
|
|
ad77ae1c0b | 1 month ago |
12 changed files with 567 additions and 19 deletions
@ -0,0 +1,11 @@ |
|||||||
|
import request from "@/api/request"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 分页查询系统访问日志。 |
||||||
|
*/ |
||||||
|
export function listSystemLog(query) { |
||||||
|
return request.get({ |
||||||
|
url: "/systemLog", |
||||||
|
query, |
||||||
|
}); |
||||||
|
} |
||||||
@ -0,0 +1,314 @@ |
|||||||
|
<template> |
||||||
|
<div class="container"> |
||||||
|
<header class="mb-20"> |
||||||
|
<el-form :label-width="100"> |
||||||
|
<el-row :gutter="12"> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-form-item label="请求人"> |
||||||
|
<el-input |
||||||
|
v-model="query.requester" |
||||||
|
placeholder="请输入登录名或姓名" |
||||||
|
clearable |
||||||
|
@keyup.enter="search" |
||||||
|
/> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-form-item label="接口地址"> |
||||||
|
<el-input |
||||||
|
v-model="query.requestUri" |
||||||
|
placeholder="请输入接口地址" |
||||||
|
clearable |
||||||
|
@keyup.enter="search" |
||||||
|
/> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-form-item label="访问方式"> |
||||||
|
<el-select |
||||||
|
v-model="query.requestMethod" |
||||||
|
placeholder="请选择访问方式" |
||||||
|
clearable |
||||||
|
> |
||||||
|
<el-option |
||||||
|
v-for="method in requestMethods" |
||||||
|
:key="method" |
||||||
|
:label="method" |
||||||
|
:value="method" |
||||||
|
/> |
||||||
|
</el-select> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-form-item label="来源IP"> |
||||||
|
<el-input |
||||||
|
v-model="query.sourceIp" |
||||||
|
placeholder="请输入来源IP" |
||||||
|
clearable |
||||||
|
@keyup.enter="search" |
||||||
|
/> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
<el-row :gutter="12"> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-form-item label="执行结果"> |
||||||
|
<el-select |
||||||
|
v-model="query.status" |
||||||
|
placeholder="请选择执行结果" |
||||||
|
clearable |
||||||
|
> |
||||||
|
<el-option label="成功" :value="1" /> |
||||||
|
<el-option label="失败" :value="0" /> |
||||||
|
</el-select> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
<el-col :span="12"> |
||||||
|
<el-form-item label="日志时间"> |
||||||
|
<el-date-picker |
||||||
|
v-model="query.logTimeRange" |
||||||
|
type="datetimerange" |
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" |
||||||
|
format="YYYY-MM-DD HH:mm:ss" |
||||||
|
range-separator="~" |
||||||
|
start-placeholder="开始时间" |
||||||
|
end-placeholder="结束时间" |
||||||
|
clearable |
||||||
|
/> |
||||||
|
</el-form-item> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
</el-form> |
||||||
|
<div class="flex end"> |
||||||
|
<el-button type="primary" @click="search"> |
||||||
|
<template #icon> |
||||||
|
<icon name="el-icon-Search" /> |
||||||
|
</template> |
||||||
|
查询 |
||||||
|
</el-button> |
||||||
|
<el-button @click="reset">重置</el-button> |
||||||
|
</div> |
||||||
|
</header> |
||||||
|
|
||||||
|
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
||||||
|
<el-tab-pane label="登录日志" name="login" /> |
||||||
|
<el-tab-pane label="操作日志" name="operation" /> |
||||||
|
</el-tabs> |
||||||
|
|
||||||
|
<div class="table-container"> |
||||||
|
<el-table :data="logs"> |
||||||
|
<el-table-column label="请求人" min-width="160"> |
||||||
|
<template #default="{ row }"> |
||||||
|
{{ formatRequester(row) }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
label="接口地址" |
||||||
|
prop="requestUri" |
||||||
|
min-width="220" |
||||||
|
show-overflow-tooltip |
||||||
|
/> |
||||||
|
<el-table-column |
||||||
|
label="访问方式" |
||||||
|
prop="requestMethod" |
||||||
|
width="100" |
||||||
|
align="center" |
||||||
|
/> |
||||||
|
<el-table-column |
||||||
|
label="来源IP" |
||||||
|
prop="sourceIp" |
||||||
|
min-width="140" |
||||||
|
show-overflow-tooltip |
||||||
|
/> |
||||||
|
<el-table-column label="请求参数" min-width="220"> |
||||||
|
<template #default="{ row }"> |
||||||
|
<span class="summary-text">{{ formatSummary(row.requestParams) }}</span> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column label="执行耗时" width="110" align="center"> |
||||||
|
<template #default="{ row }"> |
||||||
|
{{ row.executionTime }} ms |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column label="执行结果" width="100" align="center"> |
||||||
|
<template #default="{ row }"> |
||||||
|
<el-tag :type="row.status === 1 ? 'success' : 'danger'"> |
||||||
|
{{ row.status === 1 ? "成功" : "失败" }} |
||||||
|
</el-tag> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
label="日志时间" |
||||||
|
prop="logTime" |
||||||
|
width="180" |
||||||
|
/> |
||||||
|
<el-table-column label="操作" width="100" fixed="right"> |
||||||
|
<template #default="{ row }"> |
||||||
|
<el-button type="primary" link @click="showDetail(row)"> |
||||||
|
查看详情 |
||||||
|
</el-button> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
</el-table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="flex end mt-8"> |
||||||
|
<el-pagination |
||||||
|
v-model:page-size="query.size" |
||||||
|
v-model:current-page="query.current" |
||||||
|
:page-sizes="[10, 20, 50]" |
||||||
|
:total="total" |
||||||
|
layout="total, sizes, prev, pager, next" |
||||||
|
@size-change="handleSizeChange" |
||||||
|
@current-change="getList" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<el-dialog v-model="detailVisible" title="系统日志详情" width="760px"> |
||||||
|
<el-descriptions :column="2" border> |
||||||
|
<el-descriptions-item label="请求人"> |
||||||
|
{{ formatRequester(activeLog) }} |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="日志时间"> |
||||||
|
{{ activeLog.logTime || "-" }} |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="接口地址" :span="2"> |
||||||
|
{{ activeLog.requestUri || "-" }} |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="访问方式"> |
||||||
|
{{ activeLog.requestMethod || "-" }} |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="来源IP"> |
||||||
|
{{ activeLog.sourceIp || "-" }} |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="执行耗时"> |
||||||
|
{{ activeLog.executionTime ?? 0 }} ms |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="执行结果"> |
||||||
|
<el-tag :type="activeLog.status === 1 ? 'success' : 'danger'"> |
||||||
|
{{ activeLog.status === 1 ? "成功" : "失败" }} |
||||||
|
</el-tag> |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="请求参数" :span="2"> |
||||||
|
<pre class="detail-content">{{ activeLog.requestParams || "-" }}</pre> |
||||||
|
</el-descriptions-item> |
||||||
|
<el-descriptions-item label="异常信息" :span="2"> |
||||||
|
<pre class="detail-content error-content">{{ activeLog.errorMessage || "-" }}</pre> |
||||||
|
</el-descriptions-item> |
||||||
|
</el-descriptions> |
||||||
|
<template #footer> |
||||||
|
<el-button @click="detailVisible = false">关闭</el-button> |
||||||
|
</template> |
||||||
|
</el-dialog> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import { listSystemLog } from "@/api/system/systemLog"; |
||||||
|
|
||||||
|
const requestMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]; |
||||||
|
|
||||||
|
const createDefaultQuery = () => ({ |
||||||
|
current: 1, |
||||||
|
size: 10, |
||||||
|
requester: undefined, |
||||||
|
requestUri: undefined, |
||||||
|
requestMethod: undefined, |
||||||
|
sourceIp: undefined, |
||||||
|
status: undefined, |
||||||
|
logTimeRange: [], |
||||||
|
}); |
||||||
|
|
||||||
|
const query = ref(createDefaultQuery()); |
||||||
|
const logs = ref([]); |
||||||
|
const total = ref(0); |
||||||
|
const detailVisible = ref(false); |
||||||
|
const activeLog = ref({}); |
||||||
|
const activeTab = ref("login"); |
||||||
|
|
||||||
|
/** |
||||||
|
* 将页面时间范围拆分成后端接收的开始、结束时间参数。 |
||||||
|
*/ |
||||||
|
function buildQueryParams() { |
||||||
|
const { logTimeRange, ...params } = query.value; |
||||||
|
params.logType = activeTab.value; |
||||||
|
if (logTimeRange?.length === 2) { |
||||||
|
params.startTime = logTimeRange[0]; |
||||||
|
params.endTime = logTimeRange[1]; |
||||||
|
} |
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
function getList() { |
||||||
|
listSystemLog(buildQueryParams()).then((data) => { |
||||||
|
logs.value = data.records; |
||||||
|
total.value = data.total; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function search() { |
||||||
|
query.value.current = 1; |
||||||
|
getList(); |
||||||
|
} |
||||||
|
|
||||||
|
function reset() { |
||||||
|
query.value = createDefaultQuery(); |
||||||
|
getList(); |
||||||
|
} |
||||||
|
|
||||||
|
function handleSizeChange() { |
||||||
|
query.value.current = 1; |
||||||
|
getList(); |
||||||
|
} |
||||||
|
|
||||||
|
function handleTabChange() { |
||||||
|
query.value.current = 1; |
||||||
|
getList(); |
||||||
|
} |
||||||
|
|
||||||
|
function formatRequester(row = {}) { |
||||||
|
if (row.nickName && row.userName) { |
||||||
|
return `${row.nickName}(${row.userName})`; |
||||||
|
} |
||||||
|
return row.nickName || row.userName || "匿名/外部调用"; |
||||||
|
} |
||||||
|
|
||||||
|
function formatSummary(value) { |
||||||
|
if (!value) { |
||||||
|
return "-"; |
||||||
|
} |
||||||
|
return value.length > 60 ? `${value.slice(0, 60)}...` : value; |
||||||
|
} |
||||||
|
|
||||||
|
function showDetail(row) { |
||||||
|
activeLog.value = row; |
||||||
|
detailVisible.value = true; |
||||||
|
} |
||||||
|
|
||||||
|
getList(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
.summary-text { |
||||||
|
display: inline-block; |
||||||
|
max-width: 100%; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
vertical-align: middle; |
||||||
|
} |
||||||
|
|
||||||
|
.detail-content { |
||||||
|
margin: 0; |
||||||
|
max-height: 260px; |
||||||
|
overflow: auto; |
||||||
|
white-space: pre-wrap; |
||||||
|
overflow-wrap: anywhere; |
||||||
|
font-family: inherit; |
||||||
|
line-height: 1.6; |
||||||
|
} |
||||||
|
|
||||||
|
.error-content { |
||||||
|
color: var(--el-color-danger); |
||||||
|
} |
||||||
|
</style> |
||||||
Loading…
Reference in new issue