From b3a43e63d2c2fa854d676676d3f8072c0d943d13 Mon Sep 17 00:00:00 2001
From: 潘志宝 <979469083@qq.com>
Date: 星期三, 26 二月 2025 15:36:11 +0800
Subject: [PATCH] Merge branch 'master' of http://dlindusit.com:53929/r/iailab-plat-ui-vue3

---
 src/components/MonitorDiskPie/PieChart.vue             |   35 +
 /dev/null                                              |  161 -----
 src/views/infra/monitor/components/MonitorDisk.vue     |  487 +++++++++++++++++
 src/views/infra/monitor/components/MonitorMem.vue      |  478 +++++++++++++++++
 src/api/infra/monitordisk/index.ts                     |   57 ++
 src/views/infra/monitor/index.vue                      |   20 
 src/api/infra/monitormem/index.ts                      |   56 ++
 src/store/modules/mall/kefu.ts                         |   81 ++
 src/views/infra/monitor/components/index.ts            |    3 
 src/views/infra/monitor/components/MonitorDiskForm.vue |  128 ++++
 src/views/infra/monitor/components/MonitorMemForm.vue  |  148 +++++
 src/views/system/loginlog/LoginLogDetail.vue           |    2 
 12 files changed, 1,494 insertions(+), 162 deletions(-)

diff --git a/src/api/infra/monitordisk/index.ts b/src/api/infra/monitordisk/index.ts
new file mode 100644
index 0000000..e9a2512
--- /dev/null
+++ b/src/api/infra/monitordisk/index.ts
@@ -0,0 +1,57 @@
+import request from '@/config/axios'
+
+// 磁盘监控日志 VO
+export interface MonitorDiskVO {
+  id: number // 访问ID
+  hostName: string // 主机名称
+  hostIp: string // 服务器ip
+  disk: string // 盘符
+  diskName: string // 磁盘名
+  spaceTotal: number // 总空间
+  spaceUsed: number // 已用空间
+  spaceUsable: number // 可用空间
+  spaceRatio: number // 空间使用比例
+}
+
+// 磁盘监控日志 API
+export const MonitorDiskApi = {
+  // 查询磁盘监控日志分页
+  getMonitorDiskPage: async (params: any) => {
+    return await request.get({ url: `/infra/monitor-disk/page`, params })
+  },
+
+  // 查询磁盘监控日志列表
+  getMonitorDiskList: async (params: any) => {
+    return await request.get({ url: `/infra/monitor-disk/getMonitorDiskList`, params })
+  },
+
+  // 查询磁盘监控日志信息
+  getMonitorDiskInfo: async (params: any) => {
+    return await request.get({ url: `/infra/monitor-disk/getMonitorDiskInfo`, params })
+  },
+
+  // 查询磁盘监控日志详情
+  getMonitorDisk: async (id: number) => {
+    return await request.get({ url: `/infra/monitor-disk/get?id=` + id })
+  },
+
+  // 新增磁盘监控日志
+  createMonitorDisk: async (data: MonitorDiskVO) => {
+    return await request.post({ url: `/infra/monitor-disk/create`, data })
+  },
+
+  // 修改磁盘监控日志
+  updateMonitorDisk: async (data: MonitorDiskVO) => {
+    return await request.put({ url: `/infra/monitor-disk/update`, data })
+  },
+
+  // 删除磁盘监控日志
+  deleteMonitorDisk: async (id: number) => {
+    return await request.delete({ url: `/infra/monitor-disk/delete?id=` + id })
+  },
+
+  // 导出磁盘监控日志 Excel
+  exportMonitorDisk: async (params) => {
+    return await request.download({ url: `/infra/monitor-disk/export-excel`, params })
+  },
+}
diff --git a/src/api/infra/monitormem/index.ts b/src/api/infra/monitormem/index.ts
new file mode 100644
index 0000000..c402272
--- /dev/null
+++ b/src/api/infra/monitormem/index.ts
@@ -0,0 +1,56 @@
+import request from '@/config/axios'
+
+// 内存监控日志 VO
+export interface MonitorMemVO {
+  id: number // 访问ID
+  hostName: string // 主机名称
+  hostIp: string // 服务器ip
+  serverName: string // 服务名
+  physicalTotal: number // 总物理内存
+  physicalUsed: number // 已用物理内存
+  physicalFree: number // 剩余物理内存
+  physicalUsage: number // 物理内存使用率
+  runtimeTotal: number // jvm运行总内存
+  runtimeMax: number // jvm最大内存
+  runtimeUsed: number // jvm已用内存
+  runtimeFree: number // jvm空闲内存
+  runtimeUsage: number // jvm内存使用率
+}
+
+// 内存监控日志 API
+export const MonitorMemApi = {
+  // 查询内存监控日志分页
+  getMonitorMemPage: async (params: any) => {
+    return await request.get({ url: `/infra/monitor-mem/page`, params })
+  },
+
+  // 查询统计数据列表
+  getMonitorMemList: async (params: any) => {
+    return await request.get({ url: `/infra/monitor-mem/getMonitorMemList`, params })
+  },
+
+  // 查询内存监控日志详情
+  getMonitorMem: async (id: number) => {
+    return await request.get({ url: `/infra/monitor-mem/get?id=` + id })
+  },
+
+  // 新增内存监控日志
+  createMonitorMem: async (data: MonitorMemVO) => {
+    return await request.post({ url: `/infra/monitor-mem/create`, data })
+  },
+
+  // 修改内存监控日志
+  updateMonitorMem: async (data: MonitorMemVO) => {
+    return await request.put({ url: `/infra/monitor-mem/update`, data })
+  },
+
+  // 删除内存监控日志
+  deleteMonitorMem: async (id: number) => {
+    return await request.delete({ url: `/infra/monitor-mem/delete?id=` + id })
+  },
+
+  // 导出内存监控日志 Excel
+  exportMonitorMem: async (params) => {
+    return await request.download({ url: `/infra/monitor-mem/export-excel`, params })
+  },
+}
diff --git a/src/assets/svgs/member_balance.svg b/src/assets/svgs/member_balance.svg
deleted file mode 100644
index 5395b23..0000000
--- a/src/assets/svgs/member_balance.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028338187" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22985" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M983.8 312.7C958 251.7 921 197 874 150c-47-47-101.8-83.9-162.7-109.7C648.2 13.5 581.1 0 512 0S375.8 13.5 312.7 40.2C251.7 66 197 102.9 150 150c-47 47-83.9 101.8-109.7 162.7C13.5 375.8 0 442.9 0 512s13.5 136.2 40.2 199.3C66 772.3 102.9 827 150 874c47 47 101.8 83.9 162.7 109.7 63.1 26.7 130.2 40.2 199.3 40.2s136.2-13.5 199.3-40.2C772.3 958 827 921 874 874c47-47 83.9-101.8 109.7-162.7 26.7-63.1 40.2-130.2 40.2-199.3s-13.4-136.2-40.1-199.3z m-55.3 375.2c-22.8 53.8-55.4 102.2-96.9 143.7s-89.9 74.1-143.7 96.9C632.2 952.1 573 964 512 964s-120.2-11.9-175.9-35.5c-53.8-22.8-102.2-55.4-143.7-96.9s-74.1-89.9-96.9-143.7C71.9 632.2 60 573 60 512s11.9-120.2 35.5-175.9c22.8-53.8 55.4-102.2 96.9-143.7s89.9-74.1 143.7-96.9C391.8 71.9 451 60 512 60s120.2 11.9 175.9 35.5c53.8 22.8 102.2 55.4 143.7 96.9s74.1 89.9 96.9 143.7C952.1 391.8 964 451 964 512s-11.9 120.2-35.5 175.9z" fill="#000000" p-id="22986"></path><path d="M706 469.1H574.7l84.2-180.6c7-15 0.4-32.9-14.5-39.9-15-7-32.9-0.4-39.9 14.5L512 461.5l-92.5-198.3c-7-15-24.9-21.5-39.9-14.5s-21.5 24.9-14.5 39.9l84.2 180.6H318c-16.5 0-30 13.5-30 30s13.5 30 30 30h164v64h-92.5c-20.6 0-37.5 13.5-37.5 30s16.9 30 37.5 30H482v95c0 16.5 13.5 30 30 30s30-13.5 30-30v-95h92.5c20.6 0 37.5-13.5 37.5-30s-16.9-30-37.5-30H542v-64h164c16.5 0 30-13.5 30-30 0-16.6-13.5-30.1-30-30.1z" fill="#000000" p-id="22987"></path></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/member_expenditure_balance.svg b/src/assets/svgs/member_expenditure_balance.svg
deleted file mode 100644
index 02d498c..0000000
--- a/src/assets/svgs/member_expenditure_balance.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028553383" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28918" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M510.72 962.56C262.4 960 61.44 757.76 64 509.44 66.56 263.68 264.96 65.28 510.72 62.72c17.92 0 34.56 14.08 34.56 32s-14.08 34.56-32 34.56h-2.56C299.52 130.56 128 300.8 128 512s171.52 382.72 382.72 382.72S893.44 723.2 893.44 512c0-17.92 16.64-33.28 34.56-32 17.92 0 32 15.36 32 32 0 248.32-200.96 450.56-449.28 450.56z" fill="#000000" p-id="28919"></path><path d="M645.12 480H375.04c-17.92 0-34.56-14.08-34.56-32s14.08-34.56 32-34.56h272.64c17.92 0 33.28 16.64 32 34.56 0 17.92-14.08 32-32 32z m0 130.56H375.04c-17.92 0-33.28-16.64-32-34.56 0-17.92 15.36-32 32-32h270.08c17.92 0 33.28 16.64 32 34.56 0 16.64-14.08 32-32 32z" fill="#000000" p-id="28920"></path><path d="M510.72 746.24c-17.92 0-33.28-15.36-33.28-33.28V441.6c0-17.92 16.64-33.28 34.56-32 17.92 0 32 15.36 32 32v270.08c0 19.2-15.36 34.56-33.28 34.56z" fill="#000000" p-id="28921"></path><path d="M510.72 458.24c-8.96 0-17.92-3.84-24.32-10.24l-111.36-111.36c-14.08-12.8-15.36-33.28-2.56-47.36s33.28-15.36 47.36-2.56l2.56 2.56 111.36 111.36c12.8 12.8 12.8 34.56 0 47.36-6.4 6.4-15.36 10.24-23.04 10.24z" fill="#000000" p-id="28922"></path><path d="M510.72 458.24c-8.96 0-17.92-3.84-24.32-10.24-12.8-12.8-12.8-34.56 0-47.36l111.36-111.36c14.08-12.8 35.84-10.24 47.36 2.56 11.52 12.8 11.52 32 0 44.8L533.76 448c-6.4 6.4-15.36 10.24-23.04 10.24zM925.44 241.92c17.92 0 33.28-15.36 33.28-33.28 0-8.96-3.84-17.92-10.24-24.32l-111.36-111.36c-12.8-14.08-33.28-14.08-47.36-1.28s-14.08 33.28-1.28 47.36l1.28 1.28 111.36 111.36c7.68 6.4 15.36 10.24 24.32 10.24z" fill="#000000" p-id="28923"></path><path d="M815.36 353.28c8.96 0 17.92-3.84 24.32-10.24l111.36-111.36c12.8-14.08 10.24-35.84-2.56-47.36-12.8-11.52-32-11.52-44.8 0l-111.36 111.36c-12.8 12.8-12.8 34.56 0 47.36 5.12 6.4 14.08 10.24 23.04 10.24z" fill="#000000" p-id="28924"></path><path d="M920.32 241.92c17.92 0 34.56-14.08 34.56-32s-14.08-34.56-32-34.56H695.04c-17.92 0-33.28 16.64-32 34.56 0 17.92 15.36 32 32 32h225.28z" fill="#000000" p-id="28925"></path></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/member_level.svg b/src/assets/svgs/member_level.svg
deleted file mode 100644
index cbcc686..0000000
--- a/src/assets/svgs/member_level.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693027700643" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8876" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M936.96 385.877333l-203.434667-204.8-18.090667-7.68L308.565333 173.397333l-18.090667 7.68L87.04 385.877333c-9.728 9.898667-9.898667 25.941333-0.170667 35.84l406.869333 421.034667c4.778667 4.949333 11.434667 7.850667 18.432 7.850667 6.997333 0 13.653333-2.901333 18.432-7.850667l406.869333-421.034667C946.858667 411.648 946.688 395.776 936.96 385.877333zM868.522667 389.632l-141.994667 0-163.84-165.034667 141.994667 0L868.522667 389.632zM319.317333 224.768l143.018667 0-163.84 165.034667L155.477333 389.802667 319.317333 224.768zM176.469333 440.832l132.608 0 18.090667-7.509333 185.173333-186.538667 185.173333 186.538667 18.090667 7.509333 131.584 0L512 787.968 176.469333 440.832z" p-id="8877" fill="#000000"></path></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/member_point.svg b/src/assets/svgs/member_point.svg
deleted file mode 100644
index b849ddb..0000000
--- a/src/assets/svgs/member_point.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg t="1693027780777" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10083" width="128" height="128"><path d="M509.091764 501.653351c241.775532 0 424.086741-78.085426 424.086741-181.63992 0-103.543238-182.311209-181.628664-424.086741-181.628664S84.993766 216.471217 84.993766 320.014454C84.993766 423.568948 267.316232 501.653351 509.091764 501.653351zM509.091764 184.220698c222.908836 0 378.251833 71.561849 378.251833 135.793756S732.001623 455.818443 509.091764 455.818443c-222.920092 0-378.26309-71.573105-378.26309-135.803989S286.171672 184.220698 509.091764 184.220698z" fill="#000000" p-id="10084"></path><path d="M509.083577 694.061522c241.1155 0 422.937568-77.598332 422.937568-180.482561 0-27.169803-13.127995-52.453652-36.241412-75.131141-0.148379-0.153496-0.26606-0.320295-0.418532-0.468674-0.170892-0.166799-0.285502-0.345877-0.456395-0.51063l-0.11461 0.125867c-3.717671-3.40761-8.576329-5.608741-14.017248-5.608741-11.542894 0-20.898982 9.356089-20.898982 20.898982 0 6.110161 2.721994 11.481496 6.901177 15.302521l-0.082888 0.091074c13.948687 14.024411 21.809725 31.154557 21.809725 45.300742 0 64.785515-155.813718 136.966465-379.419426 136.966465-223.595474 0-379.410216-72.180949-379.410216-136.966465 0-16.139585 4.53734-29.952172 22.323425-45.670156 0.213871-0.204661 0.429789-0.381693 0.635473-0.594541 0.137123-0.118704 0.240477-0.233314 0.378623-0.354064l-0.084934-0.080841c3.416819-3.719718 5.623068-8.588609 5.623068-14.037714 0-11.542894-9.356089-20.898982-20.898982-20.898982-5.770424 0-10.993378 2.340301-14.773472 6.119371l-0.122797-0.118704c-23.408129 22.797215-36.594453 48.27754-36.594453 75.635631C86.158289 616.462167 267.979334 694.061522 509.083577 694.061522z" fill="#000000" p-id="10085"></path><path d="M895.577119 629.529787c-0.168846-0.164752-0.282433-0.342808-0.453325-0.50756l-0.11461 0.124843c-3.717671-3.40761-8.577353-5.608741-14.018272-5.608741-11.540847 0-20.897959 9.356089-20.897959 20.898982 0 6.110161 2.720971 11.482519 6.901177 15.302521l-0.083911 0.091074c13.94971 14.024411 21.810748 31.154557 21.810748 45.300742 0 64.787562-155.813718 136.966465-379.419426 136.966465-223.595474 0-379.410216-72.179926-379.410216-136.966465 0-16.139585 4.53734-29.952172 22.321378-45.670156 0.213871-0.202615 0.429789-0.381693 0.635473-0.594541 0.137123-0.118704 0.240477-0.233314 0.378623-0.354064l-0.084934-0.080841c3.416819-3.719718 5.623068-8.588609 5.623068-14.037714 0-11.542894-9.356089-20.898982-20.897959-20.898982-5.770424 0-10.993378 2.340301-14.773472 6.119371l-0.122797-0.118704c-23.410176 22.797215-36.594453 48.278563-36.594453 75.635631 0 102.884228 181.821045 180.482561 422.926312 180.482561 241.114476 0 422.935522-77.598332 422.935522-180.482561 0-27.166733-13.125949-52.452629-36.235272-75.127048C895.851365 629.847012 895.730615 629.681236 895.577119 629.529787z" fill="#000000" p-id="10086"></path></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/member_recharge_balance.svg b/src/assets/svgs/member_recharge_balance.svg
deleted file mode 100644
index 7519bb2..0000000
--- a/src/assets/svgs/member_recharge_balance.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028440322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25843" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 750.509317c-19.080745 0-31.801242-12.720497-31.801242-31.801242L480.198758 432.496894c0-19.080745 12.720497-31.801242 31.801242-31.801242s31.801242 12.720497 31.801242 31.801242l0 286.21118C537.440994 737.78882 524.720497 750.509317 512 750.509317z" fill="#000000" p-id="25844"></path><path d="M651.925466 534.26087 365.714286 534.26087c-19.080745 0-31.801242-12.720497-31.801242-31.801242 0-19.080745 12.720497-31.801242 31.801242-31.801242l286.21118 0c19.080745 0 31.801242 12.720497 31.801242 31.801242C683.726708 521.540373 671.006211 534.26087 651.925466 534.26087z" fill="#000000" p-id="25845"></path><path d="M651.925466 648.745342 365.714286 648.745342c-19.080745 0-31.801242-12.720497-31.801242-31.801242 0-19.080745 12.720497-31.801242 31.801242-31.801242l286.21118 0c19.080745 0 31.801242 12.720497 31.801242 31.801242C683.726708 636.024845 671.006211 648.745342 651.925466 648.745342z" fill="#000000" p-id="25846"></path><path d="M512 464.298137c-6.360248 0-19.080745 0-25.440994-6.360248L352.993789 324.372671c-12.720497-12.720497-12.720497-31.801242 0-44.521739 12.720497-12.720497 31.801242-12.720497 44.521739 0l133.565217 133.565217c12.720497 12.720497 12.720497 31.801242 0 44.521739C524.720497 464.298137 518.360248 464.298137 512 464.298137z" fill="#000000" p-id="25847"></path><path d="M512 464.298137c-6.360248 0-19.080745 0-25.440994-6.360248-12.720497-12.720497-12.720497-31.801242 0-44.521739l133.565217-133.565217c12.720497-12.720497 31.801242-12.720497 44.521739 0 12.720497 12.720497 12.720497 31.801242 0 44.521739L531.080745 457.937888C524.720497 464.298137 518.360248 464.298137 512 464.298137z" fill="#000000" p-id="25848"></path><path d="M512 1017.639752c-279.850932 0-508.819876-228.968944-508.819876-508.819876s228.968944-508.819876 508.819876-508.819876 508.819876 228.968944 508.819876 508.819876c0 25.440994 0 50.881988-6.360248 82.68323 0 19.080745-19.080745 31.801242-38.161491 25.440994-19.080745 0-31.801242-19.080745-25.440994-38.161491 6.360248-25.440994 6.360248-44.521739 6.360248-69.962733 0-248.049689-197.167702-445.217391-445.217391-445.217391S66.782609 267.130435 66.782609 515.180124s197.167702 445.217391 445.217391 445.217391c25.440994 0 57.242236 0 82.68323-6.360248 19.080745-6.360248 31.801242 6.360248 38.161491 25.440994 6.360248 19.080745-6.360248 31.801242-25.440994 38.161491C575.602484 1017.639752 543.801242 1017.639752 512 1017.639752z" fill="#000000" p-id="25849"></path><path d="M989.018634 864.993789l-318.012422 0c-19.080745 0-31.801242-12.720497-31.801242-31.801242s12.720497-31.801242 31.801242-31.801242l318.012422 0c19.080745 0 31.801242 12.720497 31.801242 31.801242S1001.73913 864.993789 989.018634 864.993789z" fill="#000000" p-id="25850"></path><path d="M830.012422 1024c-19.080745 0-31.801242-12.720497-31.801242-31.801242l0-318.012422c0-19.080745 12.720497-31.801242 31.801242-31.801242s31.801242 12.720497 31.801242 31.801242l0 318.012422C861.813665 1004.919255 842.732919 1024 830.012422 1024z" fill="#000000" p-id="25851"></path></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/money.svg b/src/assets/svgs/money.svg
deleted file mode 100644
index c1580de..0000000
--- a/src/assets/svgs/money.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>
\ No newline at end of file
diff --git a/src/assets/svgs/shopping.svg b/src/assets/svgs/shopping.svg
deleted file mode 100644
index f395bc7..0000000
--- a/src/assets/svgs/shopping.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 013.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 01-3.889 2.843 10.582 10.582 0 01-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 01-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 01-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 013.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 013.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 01-3.89 2.843 11 11 0 01-4.732 1.066 10.58 10.58 0 01-4.667-1.066 12.478 12.478 0 01-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 01-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 013.824-2.772 11.212 11.212 0 014.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 01.778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 01-2.788 8.743 1236.373 1236.373 0 00-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 01-1.88-4.478 44.128 44.128 0 01-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 01-2.14-2.558 10.416 10.416 0 01-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 011.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>
\ No newline at end of file
diff --git a/src/components/MonitorDiskPie/PieChart.vue b/src/components/MonitorDiskPie/PieChart.vue
new file mode 100644
index 0000000..b7ba35e
--- /dev/null
+++ b/src/components/MonitorDiskPie/PieChart.vue
@@ -0,0 +1,35 @@
+<template>
+  <svg :width="size" :height="size" viewBox="0 0 100 100">
+    <!-- 背景圆 -->
+    <circle cx="50" cy="50" r="50" fill="#eee"/>
+    <!-- 使用率扇形 -->
+    <path :d="arcPath" fill="#1C134B"/>
+  </svg>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+
+const props = defineProps({
+  used: { type: Number, required: true },
+  total: { type: Number, required: true },
+  size: { type: Number, default: 150 }
+});
+
+const percentage = computed(() => {
+  if (props.total === 0) return 0;
+  return (props.used / props.total) * 100;
+});
+
+const arcPath = computed(() => {
+  if (percentage.value >= 100) return '';
+
+  const angle = (percentage.value * 360) / 100;
+  const radians = (angle - 90) * Math.PI / 180;
+  const x = 50 + 50 * Math.cos(radians);
+  const y = 50 + 50 * Math.sin(radians);
+  const largeArc = angle > 180 ? 1 : 0;
+
+  return `M 50 50 L 50 0 A 50 50 0 ${largeArc} 1 ${x} ${y} L 50 50 Z`;
+});
+</script>
diff --git a/src/store/modules/mall/kefu.ts b/src/store/modules/mall/kefu.ts
new file mode 100644
index 0000000..2aecee0
--- /dev/null
+++ b/src/store/modules/mall/kefu.ts
@@ -0,0 +1,81 @@
+import { store } from '@/store'
+import { defineStore } from 'pinia'
+import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
+import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
+import { isEmpty } from '@/utils/is'
+
+interface MallKefuInfoVO {
+  conversationList: KeFuConversationRespVO[] // 会话列表
+  conversationMessageList: Map<number, KeFuMessageRespVO[]> // 会话消息
+}
+
+export const useMallKefuStore = defineStore('mall-kefu', {
+  state: (): MallKefuInfoVO => ({
+    conversationList: [],
+    conversationMessageList: new Map<number, KeFuMessageRespVO[]>() // key 会话,value 会话消息列表
+  }),
+  getters: {
+    getConversationList(): KeFuConversationRespVO[] {
+      return this.conversationList
+    },
+    getConversationMessageList(): (conversationId: number) => KeFuMessageRespVO[] | undefined {
+      return (conversationId: number) => this.conversationMessageList.get(conversationId)
+    }
+  },
+  actions: {
+    // ======================= 会话消息相关 =======================
+    /** 缓存历史消息 */
+    saveMessageList(conversationId: number, messageList: KeFuMessageRespVO[]) {
+      this.conversationMessageList.set(conversationId, messageList)
+    },
+
+    // ======================= 会话相关 =======================
+    /** 加载会话缓存列表 */
+    async setConversationList() {
+      this.conversationList = await KeFuConversationApi.getConversationList()
+      this.conversationSort()
+    },
+    /** 更新会话缓存已读 */
+    async updateConversationStatus(conversationId: number) {
+      if (isEmpty(this.conversationList)) {
+        return
+      }
+      const conversation = this.conversationList.find((item) => item.id === conversationId)
+      conversation && (conversation.adminUnreadMessageCount = 0)
+    },
+    /** 更新会话缓存 */
+    async updateConversation(conversationId: number) {
+      if (isEmpty(this.conversationList)) {
+        return
+      }
+
+      const conversation = await KeFuConversationApi.getConversation(conversationId)
+      this.deleteConversation(conversationId)
+      conversation && this.conversationList.push(conversation)
+      this.conversationSort()
+    },
+    /** 删除会话缓存 */
+    deleteConversation(conversationId: number) {
+      const index = this.conversationList.findIndex((item) => item.id === conversationId)
+      // 存在则删除
+      if (index > -1) {
+        this.conversationList.splice(index, 1)
+      }
+    },
+    conversationSort() {
+      // 按置顶属性和最后消息时间排序
+      this.conversationList.sort((a, b) => {
+        // 按照置顶排序,置顶的会在前面
+        if (a.adminPinned !== b.adminPinned) {
+          return a.adminPinned ? -1 : 1
+        }
+        // 按照最后消息时间排序,最近的会在前面
+        return (b.lastMessageTime as unknown as number) - (a.lastMessageTime as unknown as number)
+      })
+    }
+  }
+})
+
+export const useMallKefuStoreWithOut = () => {
+  return useMallKefuStore(store)
+}
diff --git a/src/views/infra/monitor/components/MonitorDisk.vue b/src/views/infra/monitor/components/MonitorDisk.vue
new file mode 100644
index 0000000..ffec629
--- /dev/null
+++ b/src/views/infra/monitor/components/MonitorDisk.vue
@@ -0,0 +1,487 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <!--      <el-form-item label="主机名称" prop="hostName">-->
+      <!--        <el-input-->
+      <!--          v-model="queryParams.hostName"-->
+      <!--          placeholder="请输入主机名称"-->
+      <!--          clearable-->
+      <!--          @keyup.enter="handleQuery"-->
+      <!--          class="!w-240px"-->
+      <!--        />-->
+      <!--      </el-form-item>-->
+      <el-form-item label="服务器IP" prop="hostIp">
+        <el-input
+          v-model="queryParams.hostIp"
+          placeholder="请输入服务器IP"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-120px"
+        />
+      </el-form-item>
+      <!--      <el-form-item label="盘符" prop="disk">-->
+      <!--        <el-input-->
+      <!--          v-model="queryParams.disk"-->
+      <!--          placeholder="请输入盘符"-->
+      <!--          clearable-->
+      <!--          @keyup.enter="handleQuery"-->
+      <!--          class="!w-240px"-->
+      <!--        />-->
+      <!--      </el-form-item>-->
+      <el-form-item label="磁盘名" prop="diskName">
+        <el-input
+          v-model="queryParams.diskName"
+          placeholder="请输入磁盘名"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-120px"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="datetimerange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-360px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px"/>
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px"/>
+          重置
+        </el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['infra:monitor-disk:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px"/>
+          新增
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          @click="handleExport"
+          :loading="exportLoading"
+          v-hasPermi="['infra:monitor-disk:export']"
+        >
+          <Icon icon="ep:download" class="mr-5px"/>
+          导出
+        </el-button>
+      </el-form-item>
+      <el-form-item style="float: right">
+        <el-button
+          v-if="showType == 'chart'"
+          type="warning"
+          style="font-weight: bold"
+          plain
+          @click="switchShow('data')">
+          <Icon icon="fa-solid:th-list" class="mr-5px"/>
+          列表展示
+        </el-button>
+        <el-button
+          v-if="showType == 'data'"
+          type="danger"
+          style="font-weight: bold"
+          plain
+          @click="switchShow('chart')">
+          <Icon icon="fa-solid:chart-pie" class="mr-5px"/>
+          图例展示
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <ContentWrap v-if="showType == 'chart'">
+    <!-- 磁盘使用率折线图 -->
+    <el-skeleton :loading="echartsLoading" animated>
+      <Echart :height="320" :options="diskChartOptions"/>
+    </el-skeleton>
+    <!-- 磁盘使用率饼图 -->
+    <h3 style="margin-top: 20px; margin-bottom: 10px">主机磁盘使用率</h3>
+    <div v-for="host in hostList" :key="host.name" class="host">
+      <div class="host-child">
+        <h4>主机名:{{ host.name }}&nbsp;&nbsp;&nbsp;&nbsp;主机IP:{{ host.ip }}</h4>
+        <el-skeleton :loading="echartsLoading" animated>
+          <div class="disks">
+            <div v-for="disk in host.disks" :key="disk.name" class="disk">
+              <h4 id="diskTitle">{{ disk.disk }}</h4>
+              <PieChart :used="disk.used" :total="disk.total" />
+              <div class="disk-info">
+                <div style="margin-bottom: 6px; font-size: 16px"><span style="color: #b9292b ;font-weight: bolder">{{ disk.total != 0 ? ((disk.used / disk.total) * 100).toFixed(1) : 0.0 }}% </span>已使用</div>
+                <div style="font-weight: bolder">{{ disk.used }}GB / {{ disk.total }}GB</div>
+              </div>
+            </div>
+          </div>
+        </el-skeleton>
+      </div>
+    </div>
+<!--    <div v-for="(host, hostIndex) in hostList" :key="hostIndex">-->
+<!--      <div style="margin-top: 10px">-->
+<!--        <el-skeleton :loading="echartsLoading" animated>-->
+<!--          {{ hostIndex }} &nbsp;&nbsp;&nbsp;&nbsp;主机名: {{ host.name }}&nbsp;&nbsp;&nbsp;&nbsp;-->
+<!--          服务器IP:{{ host.ip }}-->
+<!--          <div v-for="(disk, diskIndex) in host.disks" :key="diskIndex">-->
+<!--            <h3>{{ disk.name }}</h3>-->
+<!--            <div :ref="el => chartRefs[hostIndex][diskIndex] = el"-->
+<!--                 :style="{ width: '300px', height: '300px' }"></div>-->
+<!--          </div>-->
+<!--        </el-skeleton>-->
+<!--      </div>-->
+<!--    </div>-->
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap v-if="showType == 'data'">
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <el-table-column label="主机名称" align="center" prop="hostName"/>
+      <el-table-column label="服务器ip" align="center" prop="hostIp"/>
+      <el-table-column label="盘符" align="center" prop="disk"/>
+      <el-table-column label="磁盘名" align="center" prop="diskName"/>
+      <el-table-column label="总空间" align="center" prop="spaceTotal"/>
+      <el-table-column label="已用空间" align="center" prop="spaceUsed"/>
+      <el-table-column label="可用空间" align="center" prop="spaceUsable"/>
+      <el-table-column label="空间使用比例" align="center" prop="spaceRatio"/>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['infra:monitor-disk:query']"
+          >
+            详情
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['infra:monitor-disk:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <MonitorDiskForm ref="formRef" @success="getList"/>
+</template>
+
+<script setup lang="ts">
+import {dateFormatter} from '@/utils/formatTime'
+import download from '@/utils/download'
+import {MonitorDiskApi, MonitorDiskVO} from '@/api/infra/monitordisk'
+import MonitorDiskForm from './MonitorDiskForm.vue'
+import {EChartsOption} from "echarts";
+import * as echarts from 'echarts';
+import {formatTime} from "@/utils";
+import {formatDate} from "@vueuse/core";
+import PieChart from '@/components/MonitorDiskPie/PieChart.vue';
+
+/** 磁盘监控日志 列表 */
+defineOptions({name: 'MonitorDisk'})
+
+const message = useMessage() // 消息弹窗
+const {t} = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref<MonitorDiskVO[]>([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  hostName: undefined,
+  hostIp: undefined,
+  disk: undefined,
+  diskName: undefined,
+  spaceTotal: undefined,
+  spaceUsed: undefined,
+  spaceUsable: undefined,
+  spaceRatio: undefined,
+  createTime: [],
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+const echartsLoading = ref(true) // 图表加载中
+const showType = ref() //展示类型(chart-图例,data-数据)
+
+const hostList = ref([
+  {
+    name: 'Thinkpad-E14',
+    ip: '172.16.216.133',
+    disks: [
+      {disk: '磁盘C', used: 70, total: 200},
+      {disk: '磁盘D', used: 40, total: 60}
+    ]
+  },
+  {
+    name: 'Thinkpad-E16',
+    ip: '172.16.216.133',
+    disks: [
+      {disk: '磁盘C', used: 80, total: 500},
+      {disk: '磁盘D', used: 20, total: 500}
+    ]
+  }
+]);
+
+const chartRefs = ref([]);
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await MonitorDiskApi.getMonitorDiskPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  if (showType.value == 'data') {
+    getList()
+  } else {
+    getMonitorDiskDataList()
+    usedDiskInstance()
+  }
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await MonitorDiskApi.deleteMonitorDisk(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {
+  }
+}
+
+/** 导出按钮操作 */
+const handleExport = async () => {
+  try {
+    // 导出的二次确认
+    await message.exportConfirm()
+    // 发起导出
+    exportLoading.value = true
+    const data = await MonitorDiskApi.exportMonitorDisk(queryParams)
+    download.excel(data, '磁盘监控日志.xls')
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+/** 堆叠面积图配置 */
+const diskChartOptions = reactive<EChartsOption>({
+  title: {
+    text: '磁盘空间折线图'
+  },
+  dataset: {
+    dimensions: [],
+    source: []
+  },
+  grid: {
+    left: 30,
+    right: 20,
+    bottom: 10,
+    top: 70,
+    containLabel: true
+  },
+  legend: {
+    top: 0
+  },
+  series: [
+    {
+      name: 'disk', type: 'line',
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    }
+  ],
+  toolbox: {
+    feature: {
+      // 数据区域缩放
+      dataZoom: {
+        yAxisIndex: false // Y轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: {show: true, name: '物理内存日志图片'} // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross',
+      label: {
+        backgroundColor: '#6a7985'
+      }
+    },
+    padding: [5, 10]
+  },
+  xAxis: {
+    type: 'category',
+    boundaryGap: false,
+    axisTick: {
+      show: false
+    }
+  },
+  yAxis: {
+    name: "单位(百分比)",
+    nameTextStyle: {
+      color: "#aaa",
+      nameLocation: "start",
+    },
+  },
+}) as EChartsOption
+
+/** 查询统计数据列表 */
+const getMonitorDiskDataList = async () => {
+  const list = await MonitorDiskApi.getMonitorDiskList(queryParams)
+  if (list != null && list != undefined && list.length > 0) {
+    diskChartOptions.dataset['dimensions'] = Object.keys(list[0])
+    diskChartOptions.series = diskChartOptions.dataset['dimensions'].map(item => ({
+      name: item.name,
+      type: 'line',
+      emphasis: {
+        focus: 'series'
+      },
+      smooth: false
+    }));
+    diskChartOptions.series.splice(0, 1)
+    for (let item of list) {
+      item.createTime = formatTime(item.createTime, 'yyyy-MM-dd HH:mm:ss')
+    }
+  }
+  // 更新 Echarts 数据
+  diskChartOptions.dataset['source'] = list
+  echartsLoading.value = false
+}
+
+const usedDiskInstance = async () => {
+  const list = await MonitorDiskApi.getMonitorDiskInfo(queryParams)
+  hostList.value = list
+  // 仪表盘详情,用于显示数据。
+}
+
+/** 切换展示方式 */
+const switchShow = (type: String) => {
+  showType.value = type
+  if (showType.value == 'data') {
+    getList()
+  } else {
+    getMonitorDiskDataList()
+    usedDiskInstance()
+  }
+}
+
+let intervalId;
+/** 初始化 **/
+onMounted(() => {
+  showType.value = 'data';
+  const currentDate = new Date();
+  const previousDate = new Date(currentDate);
+  previousDate.setDate(currentDate.getDate() - 1);
+  queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss');
+  queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss');
+  intervalId = setInterval(() => {
+    if (showType.value == 'data') {
+      getList()
+    } else {
+      getMonitorDiskDataList()
+      usedDiskInstance()
+    }
+  }, 30000);
+})
+
+onUnmounted(() => {
+  clearInterval(intervalId);
+});
+</script>
+<style>
+  .host {
+    margin-bottom: 20px;
+    margin-right: 20px;
+    border-radius: 8px;
+  }
+  .host-child {
+    background: rgba(200, 200, 200, 0.3);
+    border-radius: 8px;
+    padding: 10px;
+  }
+  .disks {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
+    gap: 20px;
+    margin-top: 20px;
+  }
+  .disk {
+    width: 250px;
+    background: rgba(100, 100, 150, 0.2);
+    padding: 15px;
+    border-radius: 16px;
+    text-align: center;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+  }
+
+  #diskTitle {
+    margin-bottom: 10px
+  }
+
+  .disk-info {
+    margin-top: 10px;
+    font-size: 0.9em;
+    color: #666;
+  }
+</style>
diff --git a/src/views/infra/monitor/components/MonitorDiskForm.vue b/src/views/infra/monitor/components/MonitorDiskForm.vue
new file mode 100644
index 0000000..aeedc9c
--- /dev/null
+++ b/src/views/infra/monitor/components/MonitorDiskForm.vue
@@ -0,0 +1,128 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="主机名称" prop="hostName">
+        <el-input v-model="formData.hostName" placeholder="请输入主机名称" />
+      </el-form-item>
+      <el-form-item label="服务器ip" prop="hostIp">
+        <el-input v-model="formData.hostIp" placeholder="请输入服务器ip" />
+      </el-form-item>
+      <el-form-item label="盘符" prop="disk">
+        <el-input v-model="formData.disk" placeholder="请输入盘符" />
+      </el-form-item>
+      <el-form-item label="磁盘名" prop="diskName">
+        <el-input v-model="formData.diskName" placeholder="请输入磁盘名" />
+      </el-form-item>
+      <el-form-item label="总空间" prop="spaceTotal">
+        <el-input v-model="formData.spaceTotal" placeholder="请输入总空间" />
+      </el-form-item>
+      <el-form-item label="已用空间" prop="spaceUsed">
+        <el-input v-model="formData.spaceUsed" placeholder="请输入已用空间" />
+      </el-form-item>
+      <el-form-item label="可用空间" prop="spaceUsable">
+        <el-input v-model="formData.spaceUsable" placeholder="请输入可用空间" />
+      </el-form-item>
+      <el-form-item label="空间使用比例" prop="spaceRatio">
+        <el-input v-model="formData.spaceRatio" placeholder="请输入空间使用比例" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { MonitorDiskApi, MonitorDiskVO } from '@/api/infra/monitordisk'
+
+/** 磁盘监控日志 表单 */
+defineOptions({ name: 'MonitorDiskForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  hostName: undefined,
+  hostIp: undefined,
+  disk: undefined,
+  diskName: undefined,
+  spaceTotal: undefined,
+  spaceUsed: undefined,
+  spaceUsable: undefined,
+  spaceRatio: undefined,
+})
+const formRules = reactive({
+  hostName: [{ required: true, message: '主机名称不能为空', trigger: 'blur' }],
+  hostIp: [{ required: true, message: '服务器ip不能为空', trigger: 'blur' }],
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await MonitorDiskApi.getMonitorDisk(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  await formRef.value.validate()
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as MonitorDiskVO
+    if (formType.value === 'create') {
+      await MonitorDiskApi.createMonitorDisk(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await MonitorDiskApi.updateMonitorDisk(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    hostName: undefined,
+    hostIp: undefined,
+    disk: undefined,
+    diskName: undefined,
+    spaceTotal: undefined,
+    spaceUsed: undefined,
+    spaceUsable: undefined,
+    spaceRatio: undefined,
+  }
+  formRef.value?.resetFields()
+}
+</script>
\ No newline at end of file
diff --git a/src/views/infra/monitor/components/MonitorMem.vue b/src/views/infra/monitor/components/MonitorMem.vue
new file mode 100644
index 0000000..768af0e
--- /dev/null
+++ b/src/views/infra/monitor/components/MonitorMem.vue
@@ -0,0 +1,478 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+<!--      <el-form-item label="主机名称" prop="hostName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.hostName"-->
+<!--          placeholder="请输入主机名称"-->
+<!--          clearable-->
+<!--          @keyup.enter="handleQuery"-->
+<!--          class="!w-120px"-->
+<!--        />-->
+<!--      </el-form-item>-->
+      <el-form-item label="服务器IP" prop="hostIp">
+        <el-input
+          v-model="queryParams.hostIp"
+          placeholder="请输入服务器IP"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-120px"
+        />
+      </el-form-item>
+      <el-form-item label="服务名" prop="serverName">
+        <el-input
+          v-model="queryParams.serverName"
+          placeholder="请输入服务名"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-120px"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="datetimerange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-360px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px"/>
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px"/>
+          重置
+        </el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['infra:monitor-mem:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px"/>
+          新增
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          @click="handleExport"
+          :loading="exportLoading"
+          v-hasPermi="['infra:monitor-mem:export']"
+        >
+          <Icon icon="ep:download" class="mr-5px"/>
+          导出
+        </el-button>
+      </el-form-item>
+      <el-form-item style="float: right">
+        <el-button
+          v-if="showType == 'chart'"
+          type="warning"
+          style="font-weight: bold"
+          plain
+          @click="switchShow('data')">
+          <Icon icon="fa-solid:th-list" class="mr-5px"/>
+          列表展示
+        </el-button>
+        <el-button
+          v-if="showType == 'data'"
+          type="danger"
+          style="font-weight: bold"
+          plain
+          @click="switchShow('chart')">
+          <Icon icon="fa-solid:chart-pie" class="mr-5px"/>
+          图例展示
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <ContentWrap v-if="showType == 'chart'">
+    <!-- 物理内存折线图 -->
+    <el-skeleton :loading="echartsLoading" animated>
+      <Echart :height="320" :options="physicalChartOptions"/>
+    </el-skeleton>
+    <!-- JVM内存折线图 -->
+    <el-skeleton :loading="echartsLoading" animated>
+      <Echart style="margin-top: 20px" :height="320" :options="JVMChartOptions"/>
+    </el-skeleton>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap v-if="showType == 'data'">
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <el-table-column label="主机名称" align="center" prop="hostName"/>
+      <el-table-column label="服务器ip" align="center" prop="hostIp"/>
+      <el-table-column label="服务名" align="center" prop="serverName" width="120"/>
+      <el-table-column label="总内存" align="center" prop="physicalTotal"/>
+      <el-table-column label="已用内存" align="center" prop="physicalUsed"/>
+      <el-table-column label="空闲内存" align="center" prop="physicalFree"/>
+      <el-table-column label="内存使用率" align="center" prop="physicalUsage" width="100"/>
+      <el-table-column label="JVM占用内存" align="center" prop="runtimeTotal"/>
+      <el-table-column label="JVM最大内存" align="center" prop="runtimeMax"/>
+      <el-table-column label="JVM可用内存" align="center" prop="runtimeUsed"/>
+      <el-table-column label="JVM空闲内存" align="center" prop="runtimeFree"/>
+      <el-table-column label="JVM内存使用率" align="center" prop="runtimeUsage"/>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['infra:monitor-mem:query']"
+          >
+            详情
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['infra:monitor-mem:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <MonitorMemForm ref="formRef" @success="getList"/>
+</template>
+
+<script setup lang="ts">
+import {dateFormatter} from '@/utils/formatTime'
+import download from '@/utils/download'
+import {MonitorMemApi, MonitorMemVO} from '@/api/infra/monitormem'
+import MonitorMemForm from './MonitorMemForm.vue'
+import {EChartsOption} from "echarts";
+import {formatTime} from "@/utils";
+import {formatDate} from "@vueuse/core";
+
+/** 内存监控日志 列表 */
+defineOptions({name: 'MonitorMem'})
+
+const message = useMessage() // 消息弹窗
+const {t} = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref<MonitorMemVO[]>([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  hostName: undefined,
+  hostIp: undefined,
+  serverName: undefined,
+  physicalTotal: undefined,
+  physicalUsed: undefined,
+  physicalFree: undefined,
+  physicalUsage: undefined,
+  runtimeTotal: undefined,
+  runtimeMax: undefined,
+  runtimeUsed: undefined,
+  runtimeFree: undefined,
+  runtimeUsage: undefined,
+  createTime: [],
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+const echartsLoading = ref(true) // 图表加载中
+const showType = ref() //展示类型(chart-图例,data-数据)
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await MonitorMemApi.getMonitorMemPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  if(showType.value == 'data') {
+    getList()
+  } else {
+    getMonitorMemDataList()
+  }
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await MonitorMemApi.deleteMonitorMem(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {
+  }
+}
+
+/** 堆叠面积图配置 */
+const physicalChartOptions = reactive<EChartsOption>({
+  title: {
+    text: '物理内存折线图'
+  },
+  dataset: {
+    dimensions: ['createTime', 'physicalTotal', 'physicalUsed', 'physicalFree'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 10,
+    top: 70,
+    containLabel: true
+  },
+  legend: {
+    top: 0
+  },
+  series: [
+    {
+      name: '总物理内存', type: 'line',
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    },
+    {
+      name: '已用物理内存', type: 'line', areaStyle: {},
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    },
+    {
+      name: '剩余物理内存', type: 'line', areaStyle: {},
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    }
+  ],
+  toolbox: {
+    feature: {
+      // 数据区域缩放
+      dataZoom: {
+        yAxisIndex: false // Y轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: {show: true, name: '物理内存日志图片'} // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross',
+      label: {
+        backgroundColor: '#6a7985'
+      }
+    },
+    padding: [5, 10]
+  },
+  xAxis: {
+    type: 'category',
+    boundaryGap: false,
+    axisTick: {
+      show: false
+    }
+  },
+  yAxis: {
+    name: "单位(MB)",
+    nameTextStyle: {
+      color: "#aaa",
+      nameLocation: "start",
+    },
+  },
+}) as EChartsOption
+
+/** 堆叠面积图配置 */
+const JVMChartOptions = reactive<EChartsOption>({
+  title: {
+    text: 'JVM内存折线图'
+  },
+  dataset: {
+    dimensions: ['createTime', 'runtimeMax', 'runtimeTotal', 'runtimeUsed', 'runtimeFree'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 0,
+    top: 70,
+    containLabel: true
+  },
+  legend: {
+    top: 0
+  },
+  series: [
+    {
+      name: 'JVM最大内存', type: 'line',
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    },
+    {
+      name: 'JVM占用内存', type: 'line',
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    },
+    {
+      name: 'JVM可用内存', type: 'line', areaStyle: {},
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    },
+    {
+      name: 'JVM空闲内存', type: 'line', areaStyle: {},
+      emphasis: {
+        focus: 'series'
+      }, smooth: false
+    }
+  ],
+  toolbox: {
+    feature: {
+      // 数据区域缩放
+      dataZoom: {
+        yAxisIndex: false // Y轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: {show: true, name: 'JVM内存日志图片'} // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross',
+      label: {
+        backgroundColor: '#6a7985'
+      }
+    },
+    padding: [5, 10]
+  },
+  xAxis: {
+    type: 'category',
+    boundaryGap: false,
+    axisTick: {
+      show: false
+    }
+  },
+  yAxis: {
+    name: "单位(MB)",
+    nameTextStyle: {
+      color: "#aaa",
+      nameLocation: "start",
+    },
+  },
+}) as EChartsOption
+
+/** 查询统计数据列表 */
+const getMonitorMemDataList = async () => {
+  const list = await MonitorMemApi.getMonitorMemList(queryParams)
+  for (let item of list) {
+    item.createTime = formatTime(item.createTime, 'yyyy-MM-dd HH:mm:ss')
+  }
+  // 更新 Echarts 数据
+  if (physicalChartOptions.dataset && physicalChartOptions.dataset['source']) {
+    physicalChartOptions.dataset['source'] = list
+  }
+  if (JVMChartOptions.dataset && JVMChartOptions.dataset['source']) {
+    JVMChartOptions.dataset['source'] = list
+  }
+  echartsLoading.value = false
+}
+
+/** 切换展示方式 */
+const switchShow = (type: String) => {
+  showType.value = type
+  if(showType.value == 'data') {
+    getList()
+  } else {
+    getMonitorMemDataList()
+  }
+}
+
+/** 导出按钮操作 */
+const handleExport = async () => {
+  try {
+    // 导出的二次确认
+    await message.exportConfirm()
+    // 发起导出
+    exportLoading.value = true
+    const data = await MonitorMemApi.exportMonitorMem(queryParams)
+    download.excel(data, '内存监控日志.xls')
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+let intervalId;
+/** 初始化 **/
+onMounted(() => {
+  showType.value = 'data';
+  const currentDate = new Date();
+  const previousDate = new Date(currentDate);
+  previousDate.setDate(currentDate.getDate() - 1);
+  queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss');
+  queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss');
+  intervalId = setInterval(() => {
+    if(showType.value == 'data') {
+      getList()
+    } else {
+      getMonitorMemDataList()
+    }
+  }, 60000);
+})
+
+onUnmounted(() => {
+  clearInterval(intervalId);
+});
+</script>
diff --git a/src/views/infra/monitor/components/MonitorMemForm.vue b/src/views/infra/monitor/components/MonitorMemForm.vue
new file mode 100644
index 0000000..92d889b
--- /dev/null
+++ b/src/views/infra/monitor/components/MonitorMemForm.vue
@@ -0,0 +1,148 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="主机名称" prop="hostName">
+        <el-input v-model="formData.hostName" placeholder="请输入主机名称" />
+      </el-form-item>
+      <el-form-item label="服务器ip" prop="hostIp">
+        <el-input v-model="formData.hostIp" placeholder="请输入服务器ip" />
+      </el-form-item>
+      <el-form-item label="服务名" prop="serverName">
+        <el-input v-model="formData.serverName" placeholder="请输入服务名" />
+      </el-form-item>
+      <el-form-item label="总内存" prop="physicalTotal">
+        <el-input v-model="formData.physicalTotal" placeholder="请输入总物理内存" />
+      </el-form-item>
+      <el-form-item label="已用内存" prop="physicalUsed">
+        <el-input v-model="formData.physicalUsed" placeholder="请输入已用物理内存" />
+      </el-form-item>
+      <el-form-item label="空闲内存" prop="physicalFree">
+        <el-input v-model="formData.physicalFree" placeholder="请输入空闲内存" />
+      </el-form-item>
+      <el-form-item label="内存使用率" prop="physicalUsage">
+        <el-input v-model="formData.physicalUsage" placeholder="请输入物理内存使用率" />
+      </el-form-item>
+      <el-form-item label="jvm运行总内存" prop="runtimeTotal">
+        <el-input v-model="formData.runtimeTotal" placeholder="请输入jvm运行总内存" />
+      </el-form-item>
+      <el-form-item label="jvm最大内存" prop="runtimeMax">
+        <el-input v-model="formData.runtimeMax" placeholder="请输入jvm最大内存" />
+      </el-form-item>
+      <el-form-item label="jvm已用内存" prop="runtimeUsed">
+        <el-input v-model="formData.runtimeUsed" placeholder="请输入jvm已用内存" />
+      </el-form-item>
+      <el-form-item label="jvm空闲内存" prop="runtimeFree">
+        <el-input v-model="formData.runtimeFree" placeholder="请输入jvm空闲内存" />
+      </el-form-item>
+      <el-form-item label="jvm内存使用率" prop="runtimeUsage">
+        <el-input v-model="formData.runtimeUsage" placeholder="请输入jvm内存使用率" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { MonitorMemApi, MonitorMemVO } from '@/api/infra/monitormem'
+
+/** 内存监控日志 表单 */
+defineOptions({ name: 'MonitorMemForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  hostName: undefined,
+  hostIp: undefined,
+  serverName: undefined,
+  physicalTotal: undefined,
+  physicalUsed: undefined,
+  physicalFree: undefined,
+  physicalUsage: undefined,
+  runtimeTotal: undefined,
+  runtimeMax: undefined,
+  runtimeUsed: undefined,
+  runtimeFree: undefined,
+  runtimeUsage: undefined,
+})
+const formRules = reactive({
+  hostName: [{ required: true, message: '主机名称不能为空', trigger: 'blur' }],
+  hostIp: [{ required: true, message: '服务器ip不能为空', trigger: 'blur' }],
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await MonitorMemApi.getMonitorMem(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  await formRef.value.validate()
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as MonitorMemVO
+    if (formType.value === 'create') {
+      await MonitorMemApi.createMonitorMem(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await MonitorMemApi.updateMonitorMem(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    hostName: undefined,
+    hostIp: undefined,
+    serverName: undefined,
+    physicalTotal: undefined,
+    physicalUsed: undefined,
+    physicalFree: undefined,
+    physicalUsage: undefined,
+    runtimeTotal: undefined,
+    runtimeMax: undefined,
+    runtimeUsed: undefined,
+    runtimeFree: undefined,
+    runtimeUsage: undefined,
+  }
+  formRef.value?.resetFields()
+}
+</script>
diff --git a/src/views/infra/monitor/components/index.ts b/src/views/infra/monitor/components/index.ts
new file mode 100644
index 0000000..f329dca
--- /dev/null
+++ b/src/views/infra/monitor/components/index.ts
@@ -0,0 +1,3 @@
+import MonitorMem from './MonitorMem.vue'
+import MonitorDisk from './MonitorDisk.vue'
+export { MonitorMem, MonitorDisk }
diff --git a/src/views/infra/monitor/index.vue b/src/views/infra/monitor/index.vue
new file mode 100644
index 0000000..dc29b06
--- /dev/null
+++ b/src/views/infra/monitor/index.vue
@@ -0,0 +1,20 @@
+<template>
+  <ContentWrap>
+    <el-tabs v-model="activeName">
+      <el-tab-pane label="内存监控日志" name="monitorMem">
+        <monitor-mem ref="memInfoRef" />
+      </el-tab-pane>
+      <el-tab-pane label="硬盘监控日志" name="colum">
+        <monitor-disk ref="diskInfoRef" />
+      </el-tab-pane>
+    </el-tabs>
+  </ContentWrap>
+</template>
+<script lang="ts" setup>
+import { MonitorMem, MonitorDisk } from './components'
+
+defineOptions({ name: 'InfraMonitor' })
+
+const activeName = ref('monitorMem') // Tag 激活的窗口
+
+</script>
diff --git a/src/views/infra/storage/index_rec.vue b/src/views/infra/storage/index_rec.vue
deleted file mode 100644
index f6dfc23..0000000
--- a/src/views/infra/storage/index_rec.vue
+++ /dev/null
@@ -1,161 +0,0 @@
-<template>
-  <el-scrollbar height="calc(100vh - 88px - 40px - 50px)">
-    <el-row>
-      <!-- 磁盘使用量统计 -->
-        <el-col :span="12" class="mt-3">
-          <el-card class="ml-3" :gutter="12" shadow="hover">
-<!--            <div ref="chartRef"  style="width: 100%; height: 90%"></div>-->
-            <Echart :options="usedDiskEchartChika" :height="420" />
-<!--            <Echart :options="usedDiskEchartChika" :height="420" />-->
-          </el-card>
-        </el-col>
-    </el-row>
-  </el-scrollbar>
-</template>
-<script lang="ts" setup>
-import { ref, onMounted } from "vue";
-import * as StorageApi from '@/api/infra/storage'
-import { StorageMonitorInfoVO } from '@/api/infra/storage/types'
-const disks = ref<StorageMonitorInfoVO>()
-const disk = ref<StorageMonitorInfoVO>()
-
-// 基本信息
-const readDiskInfo = async () => {
-  const data = await StorageApi.getDiskInfo()
-  disks.value = data
-  disk.value = data[0]
-}
-
-// 内存使用情况
-const usedDiskEchartChika = reactive<any>({
-  title: {
-    // 仪表盘标题。
-    text: '磁盘使用情况',
-    left: 'center',
-    show: true, // 是否显示标题,默认 true。
-    offsetCenter: [0, '20%'], //相对于仪表盘中心的偏移位置,数组第一项是水平方向的偏移,第二项是垂直方向的偏移。可以是绝对的数值,也可以是相对于仪表盘半径的百分比。
-    color: 'yellow', // 文字的颜色,默认 #333。
-    fontSize: 20 // 文字的字体大小,默认 15。
-  },
-  toolbox: {
-    show: false,
-    feature: {
-      restore: { show: true },
-      saveAsImage: { show: true }
-    }
-  },
-  series: [
-    {
-      name: '峰值',
-      type: 'gauge',
-      min: 0,
-      max: 500,
-      splitNumber: 10,
-      //这是指针的颜色
-      color: '#F5C74E',
-      radius: '85%',
-      center: ['50%', '50%'],
-      startAngle: 225,
-      endAngle: -45,
-      axisLine: {
-        // 坐标轴线
-        lineStyle: {
-          // 属性lineStyle控制线条样式
-          color: [
-            [0.2, '#7FFF00'],
-            [0.8, '#00FFFF'],
-            [1, '#FF0000']
-          ],
-          //width: 6 外框的大小(环的宽度)
-          width: 10
-        }
-      },
-      axisTick: {
-        // 坐标轴小标记
-        //里面的线长是5(短线)
-        length: 5, // 属性length控制线长
-        lineStyle: {
-          // 属性lineStyle控制线条样式
-          color: '#76D9D7'
-        }
-      },
-      splitLine: {
-        // 分隔线
-        length: 20, // 属性length控制线长
-        lineStyle: {
-          // 属性lineStyle(详见lineStyle)控制线条样式
-          color: '#76D9D7'
-        }
-      },
-      axisLabel: {
-        color: '#76D9D7',
-        distance: 15,
-        fontSize: 15
-      },
-      pointer: {
-        // 指针的大小
-        width: 7,
-        show: true
-      },
-      detail: {
-        textStyle: {
-          fontWeight: 'normal',
-          // 里面文字下的数值大小(50)
-          fontSize: 15,
-          color: '#FFFFFF'
-        },
-        valueAnimation: true
-      },
-      progress: {
-        show: true
-      }
-    }
-  ]
-})
-
-
-/** 加载数据 */
-const getSummary = () => {
-  // 初始化命令图表
-  usedDiskInstance()
-}
-
-const usedDiskInstance = async () => {
-  try {
-    const data = await StorageApi.getDiskInfo()
-    disks.value = data
-    disk.value = data[0]
-    // data.forEach((disk) => {
-      console.log(disk.value)
-      console.log(disk.value.name)
-      console.log(disk.value!.restPPT)
-      // 仪表盘详情,用于显示数据。
-      usedDiskEchartChika.series[0].detail = {
-        show: true, // 是否显示详情,默认 true。
-        offsetCenter: [0, '50%'], // 相对于仪表盘中心的偏移位置,数组第一项是水平方向的偏移,第二项是垂直方向的偏移。可以是绝对的数值,也可以是相对于仪表盘半径的百分比。
-        color: 'auto', // 文字的颜色,默认 auto。
-        fontSize: 30, // 文字的字体大小,默认 15。
-        formatter: disk.value!.restPPT // 格式化函数或者字符串
-      }
-      console.log(disk.value.restPPT)
-      usedDiskEchartChika.series[0].data[0] = {
-        value: disk.value!.restPPT,
-        name: '磁盘消耗'
-      }
-      console.log(disk.value)
-      usedDiskEchartChika.tooltip = {
-        formatter: '{b} <br/>{a} : ' + disk.value!.restPPT
-      }
-    // })
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(() => {
-  readDiskInfo()
-  // 读取 redis 信息
-  // readDiskInfo()
-  // // 加载数据
-  getSummary()
-})
-</script>
diff --git a/src/views/system/loginlog/LoginLogDetail.vue b/src/views/system/loginlog/LoginLogDetail.vue
index ff49453..7d58978 100644
--- a/src/views/system/loginlog/LoginLogDetail.vue
+++ b/src/views/system/loginlog/LoginLogDetail.vue
@@ -16,7 +16,7 @@
       <el-descriptions-item label="浏览器">
         {{ detailData.userAgent }}
       </el-descriptions-item>
-      <el-descriptions-item label="登陆结果">
+      <el-descriptions-item label="登录结果">
         <dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="detailData.result" />
       </el-descriptions-item>
       <el-descriptions-item label="登录日期">

--
Gitblit v1.9.3