Browse Source

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

* feat: 系统日志添加查看详情
xiaoming 1 year ago
parent
commit
e6302b0f38

+ 6 - 2
.lintstagedrc

@@ -7,10 +7,14 @@
     "prettier --cache --write--parser json"
   ],
   "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}": [
     "prettier --cache --ignore-unknown --write",
-    "stylelint --fix"
+    "stylelint --fix --allow-empty-input"
   ],
   "*.md": ["prettier --cache --ignore-unknown --write"]
 }

+ 216 - 1
mock/system.ts

@@ -74,7 +74,7 @@ export default defineFakeRoute([
       };
     }
   },
-  // 用户管理-根据userId,获取对应角色id列表(userId:用户id)
+  // 用户管理-根据 userId 获取对应角色 id 列表(userId:用户id)
   {
     url: "/list-role-ids",
     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()
+        };
+      }
+    }
   }
 ]);

+ 7 - 7
package.json

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

File diff suppressed because it is too large
+ 218 - 229
pnpm-lock.yaml


+ 5 - 0
src/api/system.ts

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

+ 2 - 2
src/layout/components/appMain.vue

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

+ 0 - 1
src/style/reset.scss

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

+ 8 - 0
src/views/about/columns.tsx

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

+ 8 - 2
src/views/about/index.vue

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

+ 1 - 1
src/views/components/dialog/index.vue

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

+ 1 - 1
src/views/components/json-editor.vue

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

+ 105 - 0
src/views/monitor/logs/system/detail.vue

@@ -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>

+ 25 - 7
src/views/monitor/logs/system/hook.tsx

@@ -1,9 +1,11 @@
 import dayjs from "dayjs";
+import Detail from "./detail.vue";
 import { message } from "@/utils/message";
-import { getSystemLogsList } from "@/api/system";
+import { addDialog } from "@/components/ReDialog";
 import type { PaginationProps } from "@pureadmin/table";
 import { type Ref, reactive, ref, onMounted, toRaw } from "vue";
 import { getKeyList, useCopyToClipboard } from "@pureadmin/utils";
+import { getSystemLogsList, getSystemLogsDetail } from "@/api/system";
 import Info from "@iconify-icons/ri/question-line";
 
 export function useRole(tableRef: Ref) {
@@ -126,12 +128,12 @@ export function useRole(tableRef: Ref) {
       minWidth: 180,
       formatter: ({ requestTime }) =>
         dayjs(requestTime).format("YYYY-MM-DD HH:mm:ss")
+    },
+    {
+      label: "操作",
+      fixed: "right",
+      slot: "operation"
     }
-    // {
-    //   label: "操作",
-    //   fixed: "right",
-    //   slot: "operation"
-    // }
   ];
 
   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);
     copied.value
       ? message(`${url} 已拷贝`, { type: "success" })
@@ -185,6 +188,20 @@ export function useRole(tableRef: Ref) {
     onSearch();
   }
 
+  function onDetail(row) {
+    getSystemLogsDetail({ id: row.id }).then(res => {
+      addDialog({
+        title: "系统日志详情",
+        fullscreen: true,
+        hideFooter: true,
+        contentRenderer: () => Detail,
+        props: {
+          data: [res]
+        }
+      });
+    });
+  }
+
   async function onSearch() {
     loading.value = true;
     const { data } = await getSystemLogsList(toRaw(form));
@@ -216,6 +233,7 @@ export function useRole(tableRef: Ref) {
     pagination,
     selectedNum,
     onSearch,
+    onDetail,
     clearAll,
     resetForm,
     onbatchDel,

+ 16 - 1
src/views/monitor/logs/system/index.vue

@@ -5,6 +5,7 @@ import { getPickerShortcuts } from "../../utils";
 import { PureTableBar } from "@/components/RePureTableBar";
 import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 
+import View from "@iconify-icons/ep/view";
 import Delete from "@iconify-icons/ep/delete";
 import Refresh from "@iconify-icons/ep/refresh";
 
@@ -23,6 +24,7 @@ const {
   pagination,
   selectedNum,
   onSearch,
+  onDetail,
   clearAll,
   resetForm,
   onbatchDel,
@@ -133,7 +135,20 @@ const {
           @page-size-change="handleSizeChange"
           @page-current-change="handleCurrentChange"
           @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>
     </PureTableBar>
   </div>

Some files were not shown because too many files changed in this diff