mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 10:57:10 +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