feat: 系统日志添加查看详情 (#974)

* feat: 系统日志添加查看详情
This commit is contained in:
xiaoming 2024-03-13 20:46:37 +08:00 committed by GitHub
parent 0b9fcaca4a
commit e6302b0f38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 799 additions and 582 deletions

View File

@ -7,10 +7,14 @@
"prettier --cache --write--parser json" "prettier --cache --write--parser json"
], ],
"package.json": ["prettier --cache --write"], "package.json": ["prettier --cache --write"],
"*.vue": ["prettier --write", "eslint --cache --fix", "stylelint --fix"], "*.vue": [
"prettier --write",
"eslint --cache --fix",
"stylelint --fix --allow-empty-input"
],
"*.{css,scss,html}": [ "*.{css,scss,html}": [
"prettier --cache --ignore-unknown --write", "prettier --cache --ignore-unknown --write",
"stylelint --fix" "stylelint --fix --allow-empty-input"
], ],
"*.md": ["prettier --cache --ignore-unknown --write"] "*.md": ["prettier --cache --ignore-unknown --write"]
} }

View File

@ -74,7 +74,7 @@ export default defineFakeRoute([
}; };
} }
}, },
// 用户管理-根据userId获取对应角色id列表userId用户id // 用户管理-根据 userId 获取对应角色 id 列表userId用户id
{ {
url: "/list-role-ids", url: "/list-role-ids",
method: "post", method: "post",
@ -1196,5 +1196,220 @@ export default defineFakeRoute([
} }
}; };
} }
},
// 系统日志-根据 id 查日志详情
{
url: "/system-logs-detail",
method: "post",
response: ({ body }) => {
if (body.id == 1) {
return {
id: 1,
level: 1,
module: "菜单管理",
url: "/menu",
method: "post",
ip: faker.internet.ipv4(),
address: "中国河南省信阳市",
system: "macOS",
browser: "Chrome",
takesTime: 10,
responseHeaders: {
traceId: "1495502411171032",
"Content-Type": "application/json",
Connection: "keep-alive",
"Keep-Alive": "timeout=5",
"Content-Length": 17019
},
responseBody: {
success: true,
data: [
{
parentId: 0,
id: 400,
menuType: 0,
title: "menus.hssysMonitor",
name: "PureMonitor",
path: "/monitor",
component: "",
rank: 11,
redirect: "",
icon: "ep:monitor",
extraIcon: "",
enterTransition: "",
leaveTransition: "",
activePath: "",
auths: "",
frameSrc: "",
frameLoading: true,
keepAlive: false,
hiddenTag: false,
showLink: true,
showParent: false
},
{
parentId: 400,
id: 401,
menuType: 0,
title: "menus.hsOnlineUser",
name: "OnlineUser",
path: "/monitor/online-user",
component: "monitor/online/index",
rank: null,
redirect: "",
icon: "ri:user-voice-line",
extraIcon: "",
enterTransition: "",
leaveTransition: "",
activePath: "",
auths: "",
frameSrc: "",
frameLoading: true,
keepAlive: false,
hiddenTag: false,
showLink: true,
showParent: false
},
{
parentId: 400,
id: 402,
menuType: 0,
title: "menus.hsLoginLog",
name: "LoginLog",
path: "/monitor/login-logs",
component: "monitor/logs/login/index",
rank: null,
redirect: "",
icon: "ri:window-line",
extraIcon: "",
enterTransition: "",
leaveTransition: "",
activePath: "",
auths: "",
frameSrc: "",
frameLoading: true,
keepAlive: false,
hiddenTag: false,
showLink: true,
showParent: false
},
{
parentId: 400,
id: 403,
menuType: 0,
title: "menus.hsOperationLog",
name: "OperationLog",
path: "/monitor/operation-logs",
component: "monitor/logs/operation/index",
rank: null,
redirect: "",
icon: "ri:history-fill",
extraIcon: "",
enterTransition: "",
leaveTransition: "",
activePath: "",
auths: "",
frameSrc: "",
frameLoading: true,
keepAlive: false,
hiddenTag: false,
showLink: true,
showParent: false
},
{
parentId: 400,
id: 404,
menuType: 0,
title: "menus.hsSystemLog",
name: "SystemLog",
path: "/monitor/system-logs",
component: "monitor/logs/system/index",
rank: null,
redirect: "",
icon: "ri:file-search-line",
extraIcon: "",
enterTransition: "",
leaveTransition: "",
activePath: "",
auths: "",
frameSrc: "",
frameLoading: true,
keepAlive: false,
hiddenTag: false,
showLink: true,
showParent: false
}
]
},
requestHeaders: {
Accept: "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,eo;q=0.7",
Authorization: "Bearer eyJhbGciOiJIUzUxMiJ9.admin",
Connection: "keep-alive",
"Content-Length": 0,
Cookie:
"_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true",
Host: "192.168.2.121:8848",
Origin: "http://192.168.2.121:8848",
Referer: "http://192.168.2.121:8848/",
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
},
requestBody: {
title: "系统监控"
},
traceId: "1495502411171032",
requestTime: new Date()
};
} else if (body.id == 2) {
return {
id: 2,
level: 0,
module: "地图",
url: "/get-map-info?plateNumber=豫A59778U",
method: "get",
ip: faker.internet.ipv4(),
address: "中国广东省深圳市",
system: "Windows",
browser: "Firefox",
takesTime: 1200,
responseHeaders: {
traceId: "2280443117103208",
"Content-Type": "application/json",
Connection: "keep-alive",
"Keep-Alive": "timeout=5",
"Content-Length": 28693
},
responseBody: {
plateNumber: "豫A59778U",
driver: "子骞",
orientation: 289,
lng: 113.8564,
lat: 34.373
},
requestHeaders: {
Accept: "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,eo;q=0.7",
Authorization: "Bearer eyJhbGciOiJIUzUxMiJ9.admin",
Connection: "keep-alive",
"Content-Length": 0,
Cookie:
"_ga=GA1.1.231800979.1704562367; _ga_M74ZHEQ1M1=GS1.1.1709299375.7.1.1709299476.0.0.0; Hm_lvt_6a7dac00248d3b6ad8479d7249bb29c5=1709032753,1709359575; Hm_lvt_23a157b7d0d9867f7a51e42628f052f5=1708960489,1709485849,1709879672; authorized-token={%22accessToken%22:%22eyJhbGciOiJIUzUxMiJ9.admin%22%2C%22expires%22:1919520000000}; multiple-tabs=true",
Host: "192.168.2.121:8848",
Origin: "http://192.168.2.121:8848",
Referer: "http://192.168.2.121:8848/",
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
},
requestBody: null,
traceId: "2280443117103208",
requestTime: new Date()
};
}
}
} }
]); ]);

View File

@ -52,7 +52,7 @@
"@howdyjs/mouse-menu": "^2.1.3", "@howdyjs/mouse-menu": "^2.1.3",
"@logicflow/core": "^1.2.22", "@logicflow/core": "^1.2.22",
"@logicflow/extension": "^1.2.22", "@logicflow/extension": "^1.2.22",
"@pureadmin/descriptions": "^1.2.0", "@pureadmin/descriptions": "^1.2.1",
"@pureadmin/table": "^3.1.2", "@pureadmin/table": "^3.1.2",
"@pureadmin/utils": "^2.4.5", "@pureadmin/utils": "^2.4.5",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
@ -67,7 +67,7 @@
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"el-table-infinite-scroll": "^3.0.3", "el-table-infinite-scroll": "^3.0.3",
"element-plus": "^2.6.0", "element-plus": "^2.6.1",
"intro.js": "^7.2.0", "intro.js": "^7.2.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.11.6",
@ -104,9 +104,9 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^18.6.1", "@commitlint/cli": "^19.1.0",
"@commitlint/config-conventional": "^18.6.2", "@commitlint/config-conventional": "^19.1.0",
"@commitlint/types": "^18.6.1", "@commitlint/types": "^19.0.3",
"@eslint/js": "^8.57.0", "@eslint/js": "^8.57.0",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@iconify-icons/ep": "^1.2.12", "@iconify-icons/ep": "^1.2.12",
@ -117,7 +117,7 @@
"@types/gradient-string": "^1.1.5", "@types/gradient-string": "^1.1.5",
"@types/intro.js": "^5.1.5", "@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^20.11.24", "@types/node": "^20.11.26",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.12", "@types/qs": "^6.9.12",
@ -129,7 +129,7 @@
"autoprefixer": "^10.4.18", "autoprefixer": "^10.4.18",
"boxen": "^7.1.1", "boxen": "^7.1.1",
"cloc": "^2.11.0", "cloc": "^2.11.0",
"cssnano": "^6.0.5", "cssnano": "^6.1.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0", "eslint-define-config": "^2.1.0",

956
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -68,3 +68,8 @@ export const getOperationLogsList = (data?: object) => {
export const getSystemLogsList = (data?: object) => { export const getSystemLogsList = (data?: object) => {
return http.request<ResultTable>("post", "/system-logs", { data }); return http.request<ResultTable>("post", "/system-logs", { data });
}; };
/** 获取系统监控-系统日志-根据 id 查日志详情 */
export const getSystemLogsDetail = (data?: object) => {
return http.request<Result>("post", "/system-logs-detail", { data });
};

View File

@ -37,9 +37,9 @@ const layout = computed(() => {
const getSectionStyle = computed(() => { const getSectionStyle = computed(() => {
return [ return [
hideTabs.value && layout ? "padding-top: 48px;" : "", hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 85px;" : "", !hideTabs.value && layout ? "padding-top: 81px;" : "",
hideTabs.value && !layout.value ? "padding-top: 48px;" : "", hideTabs.value && !layout.value ? "padding-top: 48px;" : "",
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "", !hideTabs.value && !layout.value ? "padding-top: 81px;" : "",
props.fixedHeader props.fixedHeader
? "" ? ""
: `padding-top: 0;${ : `padding-top: 0;${

View File

@ -194,7 +194,6 @@ button,
cursor: default; cursor: default;
} }
img,
svg, svg,
video, video,
canvas, canvas,

View File

@ -4,6 +4,7 @@ export function useColumns() {
const columns = [ const columns = [
{ {
label: "当前版本", label: "当前版本",
minWidth: 100,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="!text-base"> <el-tag size="large" class="!text-base">
@ -14,6 +15,7 @@ export function useColumns() {
}, },
{ {
label: "最后编译时间", label: "最后编译时间",
minWidth: 120,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="!text-base"> <el-tag size="large" class="!text-base">
@ -24,6 +26,7 @@ export function useColumns() {
}, },
{ {
label: "推荐 node 版本", label: "推荐 node 版本",
minWidth: 140,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="!text-base"> <el-tag size="large" class="!text-base">
@ -34,6 +37,7 @@ export function useColumns() {
}, },
{ {
label: "推荐 pnpm 版本", label: "推荐 pnpm 版本",
minWidth: 140,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="!text-base"> <el-tag size="large" class="!text-base">
@ -44,6 +48,7 @@ export function useColumns() {
}, },
{ {
label: "完整版代码地址", label: "完整版代码地址",
minWidth: 140,
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (
@ -58,6 +63,7 @@ export function useColumns() {
}, },
{ {
label: "精简版代码地址", label: "精简版代码地址",
minWidth: 140,
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (
@ -72,6 +78,7 @@ export function useColumns() {
}, },
{ {
label: "文档地址", label: "文档地址",
minWidth: 100,
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (
@ -86,6 +93,7 @@ export function useColumns() {
}, },
{ {
label: "预览地址", label: "预览地址",
minWidth: 100,
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (

View File

@ -82,8 +82,11 @@ Object.keys(devDependencies).forEach(key => {
<el-card class="m-4 box-card" shadow="never"> <el-card class="m-4 box-card" shadow="never">
<template #header> <template #header>
<div class="card-header"> <div class="card-header flex items-center">
<span class="font-medium">生产环境依赖</span> <span class="font-medium">生产环境依赖</span>
<el-tag type="primary" effect="dark" size="small" round class="ml-1">
{{ schema.length }}
</el-tag>
</div> </div>
</template> </template>
<el-scrollbar> <el-scrollbar>
@ -114,8 +117,11 @@ Object.keys(devDependencies).forEach(key => {
<el-card class="m-4 box-card" shadow="never"> <el-card class="m-4 box-card" shadow="never">
<template #header> <template #header>
<div class="card-header"> <div class="card-header flex items-center">
<span class="font-medium">开发环境依赖</span> <span class="font-medium">开发环境依赖</span>
<el-tag type="primary" effect="dark" size="small" round class="ml-1">
{{ devSchema.length }}
</el-tag>
</div> </div>
</template> </template>
<el-scrollbar> <el-scrollbar>

View File

@ -2,8 +2,8 @@
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { h, createVNode, ref } from "vue"; import { h, createVNode, ref } from "vue";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import forms, { type FormProps } from "./form.vue";
import formPrimitive from "./formPrimitive.vue"; import formPrimitive from "./formPrimitive.vue";
import forms, { type FormProps } from "./form.vue";
import { cloneDeep, debounce } from "@pureadmin/utils"; import { cloneDeep, debounce } from "@pureadmin/utils";
import { import {
addDialog, addDialog,

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, watch } from "vue"; import { reactive, watch } from "vue";
import VueJsonPretty from "vue-json-pretty";
import "vue-json-pretty/lib/styles.css"; import "vue-json-pretty/lib/styles.css";
import VueJsonPretty from "vue-json-pretty";
defineOptions({ defineOptions({
name: "JsonEditor" name: "JsonEditor"

View File

@ -0,0 +1,105 @@
<script setup lang="tsx">
import { ref } from "vue";
import "vue-json-pretty/lib/styles.css";
import VueJsonPretty from "vue-json-pretty";
const props = defineProps({
data: {
type: Array,
default: () => []
}
});
const columns = [
{
label: "IP 地址",
prop: "ip"
},
{
label: "地点",
prop: "address"
},
{
label: "操作系统",
prop: "system"
},
{
label: "浏览器类型",
prop: "browser"
},
{
label: "所属模块",
prop: "module"
},
{
label: "请求时间",
prop: "requestTime"
},
{
label: "请求方法",
prop: "method"
},
{
label: "请求耗时",
prop: "takesTime"
},
{
label: "请求接口",
prop: "url",
copy: true
},
{
label: "TraceId",
prop: "traceId",
copy: true
}
];
const dataList = ref([
{
title: "响应头",
name: "responseHeaders",
data: (props.data[0] as any).responseHeaders
},
{
title: "响应体",
name: "responseBody",
data: (props.data[0] as any).responseBody
},
{
title: "请求头",
name: "requestHeaders",
data: (props.data[0] as any).requestHeaders
},
{
title: "请求体",
name: "requestBody",
data: (props.data[0] as any).requestBody
}
]);
</script>
<template>
<div>
<el-scrollbar>
<PureDescriptions
border
:data="props.data"
:columns="columns"
:column="5"
/>
</el-scrollbar>
<el-tabs :modelValue="'responseBody'" type="border-card" class="mt-4">
<el-tab-pane
v-for="(item, index) in dataList"
:key="index"
:name="item.name"
:label="item.title"
>
<el-scrollbar max-height="calc(100vh - 240px)">
<vue-json-pretty v-model:data="item.data" />
</el-scrollbar>
</el-tab-pane>
</el-tabs>
</div>
</template>

View File

@ -1,9 +1,11 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import Detail from "./detail.vue";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { getSystemLogsList } from "@/api/system"; import { addDialog } from "@/components/ReDialog";
import type { PaginationProps } from "@pureadmin/table"; import type { PaginationProps } from "@pureadmin/table";
import { type Ref, reactive, ref, onMounted, toRaw } from "vue"; import { type Ref, reactive, ref, onMounted, toRaw } from "vue";
import { getKeyList, useCopyToClipboard } from "@pureadmin/utils"; import { getKeyList, useCopyToClipboard } from "@pureadmin/utils";
import { getSystemLogsList, getSystemLogsDetail } from "@/api/system";
import Info from "@iconify-icons/ri/question-line"; import Info from "@iconify-icons/ri/question-line";
export function useRole(tableRef: Ref) { export function useRole(tableRef: Ref) {
@ -126,12 +128,12 @@ export function useRole(tableRef: Ref) {
minWidth: 180, minWidth: 180,
formatter: ({ requestTime }) => formatter: ({ requestTime }) =>
dayjs(requestTime).format("YYYY-MM-DD HH:mm:ss") dayjs(requestTime).format("YYYY-MM-DD HH:mm:ss")
},
{
label: "操作",
fixed: "right",
slot: "operation"
} }
// {
// label: "操作",
// fixed: "right",
// slot: "operation"
// }
]; ];
function handleSizeChange(val: number) { function handleSizeChange(val: number) {
@ -157,7 +159,8 @@ export function useRole(tableRef: Ref) {
} }
/** 拷贝请求接口,表格单元格被双击时触发 */ /** 拷贝请求接口,表格单元格被双击时触发 */
function handleCellDblclick({ url }) { function handleCellDblclick({ url }, { property }) {
if (property !== "url") return;
update(url); update(url);
copied.value copied.value
? message(`${url} 已拷贝`, { type: "success" }) ? message(`${url} 已拷贝`, { type: "success" })
@ -185,6 +188,20 @@ export function useRole(tableRef: Ref) {
onSearch(); onSearch();
} }
function onDetail(row) {
getSystemLogsDetail({ id: row.id }).then(res => {
addDialog({
title: "系统日志详情",
fullscreen: true,
hideFooter: true,
contentRenderer: () => Detail,
props: {
data: [res]
}
});
});
}
async function onSearch() { async function onSearch() {
loading.value = true; loading.value = true;
const { data } = await getSystemLogsList(toRaw(form)); const { data } = await getSystemLogsList(toRaw(form));
@ -216,6 +233,7 @@ export function useRole(tableRef: Ref) {
pagination, pagination,
selectedNum, selectedNum,
onSearch, onSearch,
onDetail,
clearAll, clearAll,
resetForm, resetForm,
onbatchDel, onbatchDel,

View File

@ -5,6 +5,7 @@ import { getPickerShortcuts } from "../../utils";
import { PureTableBar } from "@/components/RePureTableBar"; import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import View from "@iconify-icons/ep/view";
import Delete from "@iconify-icons/ep/delete"; import Delete from "@iconify-icons/ep/delete";
import Refresh from "@iconify-icons/ep/refresh"; import Refresh from "@iconify-icons/ep/refresh";
@ -23,6 +24,7 @@ const {
pagination, pagination,
selectedNum, selectedNum,
onSearch, onSearch,
onDetail,
clearAll, clearAll,
resetForm, resetForm,
onbatchDel, onbatchDel,
@ -133,7 +135,20 @@ const {
@page-size-change="handleSizeChange" @page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange" @page-current-change="handleCurrentChange"
@cell-dblclick="handleCellDblclick" @cell-dblclick="handleCellDblclick"
/> >
<template #operation="{ row }">
<el-button
class="reset-margin !outline-none"
link
type="primary"
:size="size"
:icon="useRenderIcon(View)"
@click="onDetail(row)"
>
详情
</el-button>
</template>
</pure-table>
</template> </template>
</PureTableBar> </PureTableBar>
</div> </div>