mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-11 16:57:09 +08:00
refactor: 系统管理/系统日志 => 系统监控/系统日志
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
export * from './online'
|
export * from './online'
|
||||||
|
export * from './log'
|
||||||
|
24
src/apis/monitor/log.ts
Normal file
24
src/apis/monitor/log.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
import type * as Monitor from './type'
|
||||||
|
|
||||||
|
const BASE_URL = '/system/log'
|
||||||
|
|
||||||
|
/** @desc 查询日志列表 */
|
||||||
|
export function listLog(query: Monitor.LogPageQuery) {
|
||||||
|
return http.get<PageRes<Monitor.LogResp[]>>(`${BASE_URL}`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询日志详情 */
|
||||||
|
export function getLog(id: string) {
|
||||||
|
return http.get<Monitor.LogDetailResp>(`${BASE_URL}/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导出登录日志 */
|
||||||
|
export function exportLoginLog(query: Monitor.LogQuery) {
|
||||||
|
return http.download<any>(`${BASE_URL}/export/login`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导出操作日志 */
|
||||||
|
export function exportOperationLog(query: Monitor.LogQuery) {
|
||||||
|
return http.download<any>(`${BASE_URL}/export/operation`, query)
|
||||||
|
}
|
@@ -13,8 +13,43 @@ export interface OnlineUserResp {
|
|||||||
createUserString: string
|
createUserString: string
|
||||||
createTime: string
|
createTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnlineUserQuery extends PageQuery {
|
export interface OnlineUserQuery extends PageQuery {
|
||||||
nickname?: string
|
nickname?: string
|
||||||
loginTime?: string
|
loginTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 系统日志类型 */
|
||||||
|
export interface LogResp {
|
||||||
|
id: string
|
||||||
|
description: string
|
||||||
|
module: string
|
||||||
|
timeTaken: number
|
||||||
|
ip: string
|
||||||
|
address: string
|
||||||
|
browser: string
|
||||||
|
os: string
|
||||||
|
status: number
|
||||||
|
errorMsg: string
|
||||||
|
createUserString: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
export interface LogDetailResp extends LogResp {
|
||||||
|
traceId: string
|
||||||
|
requestUrl: string
|
||||||
|
requestMethod: string
|
||||||
|
requestHeaders: string
|
||||||
|
requestBody: string
|
||||||
|
statusCode: number
|
||||||
|
responseHeaders: string
|
||||||
|
responseBody: string
|
||||||
|
}
|
||||||
|
export interface LogQuery{
|
||||||
|
description?: string
|
||||||
|
module?: string
|
||||||
|
ip?: string
|
||||||
|
createUserString?: string
|
||||||
|
createTime?: string
|
||||||
|
status?: number
|
||||||
|
sort: Array<string>
|
||||||
|
}
|
||||||
|
export interface LogPageQuery extends PageQuery, LogQuery {}
|
||||||
|
@@ -2,7 +2,7 @@ export * from './user'
|
|||||||
export * from './role'
|
export * from './role'
|
||||||
export * from './menu'
|
export * from './menu'
|
||||||
export * from './dept'
|
export * from './dept'
|
||||||
export * from './log'
|
export * from '../monitor/log'
|
||||||
export * from './dict'
|
export * from './dict'
|
||||||
export * from './file'
|
export * from './file'
|
||||||
export * from './storage'
|
export * from './storage'
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
|
||||||
|
|
||||||
const BASE_URL = '/system/log'
|
|
||||||
|
|
||||||
/** @desc 查询日志列表 */
|
|
||||||
export function listLog(query: System.PageLogQuery) {
|
|
||||||
return http.get<PageRes<System.LogResp[]>>(`${BASE_URL}`, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 查询日志详情 */
|
|
||||||
export function getLog(id: string) {
|
|
||||||
return http.get<System.LogDetailResp>(`${BASE_URL}/${id}`)
|
|
||||||
}
|
|
||||||
/** @desc 导出日志列表 */
|
|
||||||
export function exportLog(query: System.LogQuery) {
|
|
||||||
return http.download<any>(`${BASE_URL}/export/login`, query)
|
|
||||||
}
|
|
||||||
/**@desc 导出操作日志 */
|
|
||||||
export function exportOperateLog(query: System.LogQuery) {
|
|
||||||
return http.download<any>(`${BASE_URL}/export/operation`, query)
|
|
||||||
}
|
|
@@ -119,43 +119,6 @@ export interface DeptQuery {
|
|||||||
sort: Array<string>
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 系统日志类型 */
|
|
||||||
export interface LogResp {
|
|
||||||
id: string
|
|
||||||
description: string
|
|
||||||
module: string
|
|
||||||
timeTaken: number
|
|
||||||
ip: string
|
|
||||||
address: string
|
|
||||||
browser: string
|
|
||||||
os: string
|
|
||||||
status: number
|
|
||||||
errorMsg: string
|
|
||||||
createUserString: string
|
|
||||||
createTime: string
|
|
||||||
}
|
|
||||||
export interface LogDetailResp extends LogResp {
|
|
||||||
traceId: string
|
|
||||||
requestUrl: string
|
|
||||||
requestMethod: string
|
|
||||||
requestHeaders: string
|
|
||||||
requestBody: string
|
|
||||||
statusCode: number
|
|
||||||
responseHeaders: string
|
|
||||||
responseBody: string
|
|
||||||
}
|
|
||||||
// 系统日志分页查询条件
|
|
||||||
export interface PageLogQuery extends PageQuery,LogQuery{}
|
|
||||||
// 系统日志查询条件
|
|
||||||
export interface LogQuery{
|
|
||||||
description?: string
|
|
||||||
module?: string
|
|
||||||
ip?: string
|
|
||||||
createUserString?: string
|
|
||||||
createTime?: string
|
|
||||||
status?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 系统字典类型 */
|
/** 系统字典类型 */
|
||||||
export interface DictResp {
|
export interface DictResp {
|
||||||
id: string
|
id: string
|
||||||
|
@@ -1,29 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="json_prettt_container">
|
<div class="json_pretty_container">
|
||||||
<vue-json-pretty
|
<vue-json-pretty
|
||||||
:path="'res'"
|
:path="'res'"
|
||||||
:data="JSONObject"
|
:data="JSONObject"
|
||||||
:show-length="true"
|
:show-length="true"
|
||||||
/>
|
/>
|
||||||
<icon-copy class="copy_icon" @click="onCopy(JSONObject)"/>
|
<icon-copy class="copy_icon" @click="onCopy(JSONObject)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VueJsonPretty from 'vue-json-pretty'
|
import VueJsonPretty from 'vue-json-pretty'
|
||||||
import 'vue-json-pretty/lib/styles.css'
|
import 'vue-json-pretty/lib/styles.css'
|
||||||
import {copyText} from '@/utils'
|
import { copyText } from '@/utils'
|
||||||
|
|
||||||
defineOptions({ name: 'JsonPretty', inheritAttrs: false })
|
defineOptions({ name: 'JsonPretty', inheritAttrs: false })
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
josn: string
|
json: string
|
||||||
}>()
|
}>()
|
||||||
const JSONObject = computed(()=>JSON.parse(props?.josn))
|
|
||||||
const onCopy =(data:object)=>{
|
const JSONObject = computed(() => JSON.parse(props?.json))
|
||||||
|
|
||||||
|
// 拷贝
|
||||||
|
const onCopy = (data: object) => {
|
||||||
copyText(JSON.stringify(data))
|
copyText(JSON.stringify(data))
|
||||||
console.log('copyObject',data)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.json_prettt_container{
|
.json_pretty_container{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@@ -6,8 +6,8 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
@filterChange="filterChange"
|
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['setting']"
|
||||||
|
@filterChange="filterChange"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
<template #custom-left>
|
<template #custom-left>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #custom-right>
|
<template #custom-right>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button @click="onExportFile">
|
<a-button @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
@@ -45,22 +45,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listLog,exportLog } from '@/apis'
|
import { exportLoginLog, listLog } from '@/apis'
|
||||||
import type { TableInstance } from '@arco-design/web-vue'
|
import type { TableInstance } from '@arco-design/web-vue'
|
||||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
||||||
import { useTable } from '@/hooks'
|
import { useTable, useDownload } from '@/hooks'
|
||||||
import {useDownload} from '@/hooks'
|
|
||||||
defineOptions({ name: 'LoginLog' })
|
defineOptions({ name: 'LoginLog' })
|
||||||
const filterChange = (values,record)=>{
|
|
||||||
try {
|
|
||||||
const slotName = columns[values.split('_').pop()].slotName as string
|
|
||||||
const value = record.join(',')
|
|
||||||
queryForm[slotName] = value
|
|
||||||
search()
|
|
||||||
} catch (error) {
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const columns: TableInstance['columns'] = [
|
const columns: TableInstance['columns'] = [
|
||||||
{
|
{
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -95,10 +86,7 @@ const columns: TableInstance['columns'] = [
|
|||||||
{ title: '浏览器', dataIndex: 'browser', ellipsis: true, tooltip: true },
|
{ title: '浏览器', dataIndex: 'browser', ellipsis: true, tooltip: true },
|
||||||
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
||||||
]
|
]
|
||||||
//导出登录日志
|
|
||||||
const onExportFile = ()=>{
|
|
||||||
useDownload(()=>exportLog(queryForm))
|
|
||||||
}
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
module: '登录',
|
module: '登录',
|
||||||
ip: undefined,
|
ip: undefined,
|
||||||
@@ -123,6 +111,22 @@ const reset = () => {
|
|||||||
queryForm.status = undefined
|
queryForm.status = undefined
|
||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const onExport = () => {
|
||||||
|
useDownload(() => exportLoginLog(queryForm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤查询
|
||||||
|
const filterChange = (dataIndex, filteredValues) => {
|
||||||
|
try {
|
||||||
|
const slotName = columns[dataIndex.split('_').pop()].slotName as string
|
||||||
|
queryForm[slotName] = filteredValues.join(',')
|
||||||
|
search()
|
||||||
|
} catch (error) {
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
@@ -7,8 +7,8 @@
|
|||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
column-resizable
|
column-resizable
|
||||||
@filterChange="filterChange"
|
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['setting']"
|
||||||
|
@filterChange="filterChange"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
<template #custom-left>
|
<template #custom-left>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<a-button @click="reset">重置</a-button>
|
<a-button @click="reset">重置</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #custom-right>
|
<template #custom-right>
|
||||||
<a-tooltip content="导出" @click="onExportFile">
|
<a-tooltip content="导出">
|
||||||
<a-button>
|
<a-button @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
@@ -56,23 +56,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listLog, type LogResp,exportOperateLog } from '@/apis'
|
import { listLog, exportOperationLog, type LogResp } from '@/apis'
|
||||||
import type { TableInstance } from '@arco-design/web-vue'
|
import type { TableInstance } from '@arco-design/web-vue'
|
||||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
||||||
import OperationLogDetailDrawer from './OperationLogDetailDrawer.vue'
|
import OperationLogDetailDrawer from './OperationLogDetailDrawer.vue'
|
||||||
import { useDownload, useTable } from '@/hooks'
|
import { useTable, useDownload } from '@/hooks'
|
||||||
|
|
||||||
defineOptions({ name: 'OperationLog' })
|
defineOptions({ name: 'OperationLog' })
|
||||||
const filterChange = (values,record)=>{
|
|
||||||
try {
|
|
||||||
const slotName = columns[values.split('_').pop()].slotName as string
|
|
||||||
const value = record.join(',')
|
|
||||||
queryForm[slotName] = value
|
|
||||||
search()
|
|
||||||
} catch (error) {
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const columns: TableInstance['columns'] = [
|
const columns: TableInstance['columns'] = [
|
||||||
{
|
{
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -109,10 +100,7 @@ const columns: TableInstance['columns'] = [
|
|||||||
{ title: '浏览器', dataIndex: 'browser', ellipsis: true, tooltip: true },
|
{ title: '浏览器', dataIndex: 'browser', ellipsis: true, tooltip: true },
|
||||||
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
||||||
]
|
]
|
||||||
//导出操作日志
|
|
||||||
const onExportFile = ()=>{
|
|
||||||
useDownload(()=>exportOperateLog(queryForm))
|
|
||||||
}
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
description: undefined,
|
description: undefined,
|
||||||
ip: undefined,
|
ip: undefined,
|
||||||
@@ -139,6 +127,22 @@ const reset = () => {
|
|||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const onExport = () => {
|
||||||
|
useDownload(() => exportOperationLog(queryForm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤查询
|
||||||
|
const filterChange = (dataIndex, filteredValues) => {
|
||||||
|
try {
|
||||||
|
const slotName = columns[dataIndex.split('_').pop()].slotName as string
|
||||||
|
queryForm[slotName] = filteredValues.join(',')
|
||||||
|
search()
|
||||||
|
} catch (error) {
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const OperationLogDetailDrawerRef = ref<InstanceType<typeof OperationLogDetailDrawer>>()
|
const OperationLogDetailDrawerRef = ref<InstanceType<typeof OperationLogDetailDrawer>>()
|
||||||
// 查询详情
|
// 查询详情
|
||||||
const openDetail = (item: LogResp) => {
|
const openDetail = (item: LogResp) => {
|
106
src/views/monitor/log/OperationLogDetailDrawer.vue
Normal file
106
src/views/monitor/log/OperationLogDetailDrawer.vue
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer v-model:visible="visible" title="日志详情" :width="720" :footer="false">
|
||||||
|
<a-descriptions title="基本信息" :column="2" size="large" class="general-description">
|
||||||
|
<a-descriptions-item label="日志 ID">{{ dataDetail?.id }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="Trace ID" >{{ dataDetail?.traceId }}<TextCopy :value="dataDetail?.traceId" /></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作人">{{ dataDetail?.createUserString }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作时间">{{ dataDetail?.createTime }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作内容">{{ dataDetail?.description }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="所属模块">{{ dataDetail?.module }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作 IP">{{ dataDetail?.ip }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作地点">{{ dataDetail?.address }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="浏览器">{{ dataDetail?.browser }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="终端系统">{{ dataDetail?.os }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="状态">
|
||||||
|
<a-tag v-if="dataDetail?.status === 1" color="green">成功</a-tag>
|
||||||
|
<a-tag v-else color="red">失败</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="耗时">
|
||||||
|
<a-tag v-if="dataDetail?.timeTaken > 500" color="red">
|
||||||
|
{{ dataDetail?.timeTaken }}ms
|
||||||
|
</a-tag>
|
||||||
|
<a-tag v-else-if="dataDetail?.timeTaken > 200" color="orange">
|
||||||
|
{{ dataDetail?.timeTaken }}ms
|
||||||
|
</a-tag>
|
||||||
|
<a-tag v-else color="green">{{ dataDetail?.timeTaken }} ms</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求 URI" :span="2">
|
||||||
|
{{ dataDetail?.requestUrl }}<TextCopy :value="dataDetail?.requestUrl" />
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
<a-descriptions
|
||||||
|
title="响应信息"
|
||||||
|
:column="2"
|
||||||
|
size="large"
|
||||||
|
class="general-description http"
|
||||||
|
style="margin-top: 20px; position: relative"
|
||||||
|
>
|
||||||
|
<a-descriptions-item :span="2">
|
||||||
|
<a-tabs type="card">
|
||||||
|
<a-tab-pane key="1" title="响应头">
|
||||||
|
<JsonPretty v-if="dataDetail?.responseHeaders" :json="dataDetail?.responseHeaders" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" title="响应体">
|
||||||
|
<JsonPretty v-if="dataDetail?.responseBody" :json="dataDetail?.responseBody" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
<a-descriptions
|
||||||
|
title="请求信息"
|
||||||
|
:column="2"
|
||||||
|
size="large"
|
||||||
|
class="general-description http"
|
||||||
|
style="margin-top: 20px; position: relative"
|
||||||
|
>
|
||||||
|
<a-descriptions-item :span="2">
|
||||||
|
<a-tabs type="card">
|
||||||
|
<a-tab-pane key="1" title="请求头">
|
||||||
|
<JsonPretty v-if="dataDetail?.requestHeaders" :json="dataDetail?.requestHeaders" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" title="请求体">
|
||||||
|
<JsonPretty v-if="dataDetail?.requestBody" :json="dataDetail?.requestBody" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getLog, type LogDetailResp } from '@/apis'
|
||||||
|
|
||||||
|
const dataId = ref('')
|
||||||
|
const dataDetail = ref<LogDetailResp>()
|
||||||
|
|
||||||
|
// 查询详情
|
||||||
|
const getDataDetail = async () => {
|
||||||
|
const res = await getLog(dataId.value)
|
||||||
|
dataDetail.value = res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
// 打开详情
|
||||||
|
const open = async (id: string) => {
|
||||||
|
dataId.value = id
|
||||||
|
await getDataDetail()
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.http :deep(.arco-descriptions-item-label-block) {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tabs-content) {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gi_page">
|
<div class="gi_page">
|
||||||
<a-card title="系统日志" class="general-card">
|
<a-card title="系统日志" class="general-card">
|
||||||
<a-tabs type="card-gutter" size="large" :active-key="activeKey" @change="change">
|
<a-tabs type="card-gutter" size="large" :active-key="activeKey" @change="change">
|
||||||
<a-tab-pane key="1" title="登录日志"/>
|
<a-tab-pane key="1" title="登录日志" />
|
||||||
<a-tab-pane key="2" title="操作日志"/>
|
<a-tab-pane key="2" title="操作日志" />
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="PaneMap[activeKey]"></component>
|
<component :is="PaneMap[activeKey]" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,12 +15,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LoginLog from './LoginLog.vue'
|
import LoginLog from './LoginLog.vue'
|
||||||
import OperationLog from './OperationLog.vue'
|
import OperationLog from './OperationLog.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const PaneMap:Record<string,Component> = {
|
|
||||||
|
const PaneMap: Record<string, Component> = {
|
||||||
'1': LoginLog,
|
'1': LoginLog,
|
||||||
'2': OperationLog
|
'2': OperationLog
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeKey = ref('1')
|
const activeKey = ref('1')
|
||||||
watch(
|
watch(
|
||||||
() => route.query,
|
() => route.query,
|
||||||
@@ -31,6 +34,7 @@ watch(
|
|||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const change = (key: string | number) => {
|
const change = (key: string | number) => {
|
||||||
activeKey.value = key as string
|
activeKey.value = key as string
|
||||||
router.replace({ path: route.path, query: { tabKey: key } })
|
router.replace({ path: route.path, query: { tabKey: key } })
|
@@ -1,105 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-drawer v-model:visible="visible" title="日志详情" :width="720" :footer="false">
|
|
||||||
<a-descriptions title="基本信息" :column="2" size="large" class="general-description">
|
|
||||||
<a-descriptions-item label="日志 ID">{{ operationLog?.id }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="Trace ID" >{{ operationLog?.traceId }}<TextCopy :value="operationLog?.traceId"/></a-descriptions-item>
|
|
||||||
<a-descriptions-item label="操作人">{{ operationLog?.createUserString }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="操作时间">{{ operationLog?.createTime }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="操作内容">{{ operationLog?.description }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="所属模块">{{ operationLog?.module }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="操作 IP">{{ operationLog?.ip }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="操作地点">{{ operationLog?.address }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="浏览器">{{ operationLog?.browser }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="终端系统">{{ operationLog?.os }}</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="状态">
|
|
||||||
<a-tag v-if="operationLog?.status === 1" color="green">成功</a-tag>
|
|
||||||
<a-tag v-else color="red">失败</a-tag>
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="耗时">
|
|
||||||
<a-tag v-if="operationLog?.timeTaken > 500" color="red">
|
|
||||||
{{ operationLog?.timeTaken }}ms
|
|
||||||
</a-tag>
|
|
||||||
<a-tag v-else-if="operationLog?.timeTaken > 200" color="orange">
|
|
||||||
{{ operationLog?.timeTaken }}ms
|
|
||||||
</a-tag>
|
|
||||||
<a-tag v-else color="green">{{ operationLog?.timeTaken }} ms</a-tag>
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="请求 URI" :span="2">
|
|
||||||
{{ operationLog?.requestUrl }}<TextCopy :value="operationLog?.requestUrl"/>
|
|
||||||
</a-descriptions-item>
|
|
||||||
</a-descriptions>
|
|
||||||
<a-descriptions
|
|
||||||
title="响应信息"
|
|
||||||
:column="2"
|
|
||||||
size="large"
|
|
||||||
class="general-description http"
|
|
||||||
style="margin-top: 20px; position: relative"
|
|
||||||
>
|
|
||||||
<a-descriptions-item :span="2">
|
|
||||||
<a-tabs type="card">
|
|
||||||
<a-tab-pane key="1" title="响应头">
|
|
||||||
<JsonPretty v-if="operationLog?.responseHeaders" :josn="operationLog?.responseHeaders"/>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</a-tab-pane>
|
|
||||||
<a-tab-pane key="2" title="响应体">
|
|
||||||
<JsonPretty v-if="operationLog?.responseBody" :josn="operationLog?.responseBody"/>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</a-descriptions-item>
|
|
||||||
</a-descriptions>
|
|
||||||
<a-descriptions
|
|
||||||
title="请求信息"
|
|
||||||
:column="2"
|
|
||||||
size="large"
|
|
||||||
class="general-description http"
|
|
||||||
style="margin-top: 20px; position: relative"
|
|
||||||
>
|
|
||||||
<a-descriptions-item :span="2">
|
|
||||||
<a-tabs type="card">
|
|
||||||
<a-tab-pane key="1" title="请求头">
|
|
||||||
<JsonPretty v-if="operationLog?.requestHeaders" :josn="operationLog?.requestHeaders"/>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</a-tab-pane>
|
|
||||||
<a-tab-pane key="2" title="请求体">
|
|
||||||
<JsonPretty v-if="operationLog?.requestBody" :josn="operationLog?.requestBody"/>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</a-descriptions-item>
|
|
||||||
</a-descriptions>
|
|
||||||
</a-drawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { getLog, type LogDetailResp } from '@/apis'
|
|
||||||
import JsonPretty from '@/components/JsonPretty/index.vue'
|
|
||||||
const logId = ref('')
|
|
||||||
const operationLog = ref<LogDetailResp | null>()
|
|
||||||
// 查询详情
|
|
||||||
const getOperationLogDetail = async () => {
|
|
||||||
const res = await getLog(logId.value)
|
|
||||||
operationLog.value = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
const visible = ref(false)
|
|
||||||
// 打开详情
|
|
||||||
const open = async (id: string) => {
|
|
||||||
logId.value = id
|
|
||||||
await getOperationLogDetail()
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ open })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.http :deep(.arco-descriptions-item-label-block) {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.arco-tabs-content) {
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
Reference in New Issue
Block a user