From 8bb7160c9c4fd7ce5893ee673647b13cc35410ae Mon Sep 17 00:00:00 2001
From: liriming <1343021927@qq.com>
Date: 星期一, 03 三月 2025 17:27:07 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 src/views/infra/apiErrorLog/index.vue                       |   14 
 src/views/data/ind/item/CalIndDefineForm.vue                |   58 
 src/views/data/ind/item/IndHistoryChart.vue                 |  169 +
 src/views/data/point/index.vue                              |   37 
 src/views/model/pre/accuracy/index.vue                      |  146 +
 src/views/model/matlab/model/index.vue                      |  188 +
 src/views/model/mpk/file/MpkRun.vue                         |   61 
 src/views/infra/monitor/components/MonitorDiskForm.vue      |  128 +
 src/views/system/operatelog/index.vue                       |    4 
 src/views/infra/monitor/components/MonitorMemForm.vue       |  148 +
 src/views/model/matlab/model/MatlabModelForm.vue            |  394 +++
 src/views/model/matlab/project/MatlabProjectModelDialog.vue |  101 
 src/views/data/arc/ArcSettingForm.vue                       |  119 
 src/views/model/pre/accuracy/MmItemAccuracyRateForm.vue     |  176 +
 src/views/data/plan/data/DataSetForm.vue                    |   39 
 src/views/system/loginlog/LoginLogDetail.vue                |    2 
 src/components/MonitorDiskPie/PieChart.vue                  |   35 
 src/router/modules/remaining.ts                             |   23 
 src/api/data/ind/item/item.ts                               |   13 
 src/api/model/pre/alarm/message.ts                          |   28 
 src/api/model/pre/item/index.ts                             |   10 
 src/views/model/pre/alarm/MmPredictAlarmMessageForm.vue     |  135 +
 src/views/data/point/DaPointValue.vue                       |  144 +
 src/views/infra/monitor/components/MonitorDisk.vue          |  487 ++++
 src/views/model/pre/alarm/MmPredictAlarmConfigForm.vue      |  239 ++
 src/utils/dict.ts                                           |    5 
 src/api/model/sche/model/index.ts                           |   20 
 src/views/model/pre/alarm/index.vue                         |  178 +
 src/api/model/pre/alarm/config.ts                           |   57 
 src/views/data/point/DaPointForm.vue                        |    1 
 src/api/infra/monitordisk/index.ts                          |   57 
 src/api/data/arc/data.ts                                    |    2 
 src/views/infra/monitor/index.vue                           |   20 
 src/api/model/pre/accuracy/his.ts                           |   19 
 src/views/model/pre/item/MmPredictItemChart.vue             |   49 
 src/views/data/ind/item/IndCurrentData.vue                  |   45 
 src/views/data/arc/index.vue                                |  307 +-
 src/views/data/ind/item/AtomIndDefineForm.vue               |   68 
 src/views/infra/apiAccessLog/index.vue                      |   14 
 src/api/data/da/point/daPointChart.ts                       |   14 
 src/utils/download.ts                                       |    5 
 src/api/model/matlab/project.ts                             |   33 
 src/views/data/ind/item/index.vue                           |  250 +
 src/views/model/matlab/model/MatlabModelSettingForm.vue     |  144 +
 src/views/model/mpk/file/index.vue                          |   17 
 src/api/data/arc/index.ts                                   |   13 
 src/api/model/pre/accuracy/rate.ts                          |   50 
 src/views/infra/monitor/components/MonitorMem.vue           |  478 ++++
 src/views/system/loginlog/index.vue                         |   14 
 src/views/model/sche/scheme/record/index.vue                |    2 
 src/views/data/ind/item/DerIndDefineForm.vue                |   77 
 src/views/infra/monitor/components/index.ts                 |    3 
 src/views/model/matlab/project/MatlabProjectForm.vue        |  156 +
 src/views/model/pre/analysis/index.vue                      |  308 ++
 src/api/model/matlab/mlModel.ts                             |   29 
 src/views/model/pre/item/MmPredictItemForm.vue              |  188 +
 /dev/null                                                   |  161 -
 src/views/data/ind/data/DataSetForm.vue                     |   47 
 src/views/model/matlab/model/MatlabRun.vue                  |  279 ++
 src/views/model/matlab/project/index.vue                    |  221 +
 src/views/data/arc/ArcData.vue                              |   12 
 src/api/infra/monitormem/index.ts                           |   56 
 src/store/modules/mall/kefu.ts                              |   81 
 src/views/model/sche/model/ScheduleModelForm.vue            |  110 
 src/api/model/mpk/mpk.ts                                    |    4 
 src/views/data/point/DaPointChart.vue                       |    2 
 src/views/model/pre/accuracy/MmItemAccuracyHisForm.vue      |  122 +
 67 files changed, 5,863 insertions(+), 753 deletions(-)

diff --git a/src/api/data/arc/data.ts b/src/api/data/arc/data.ts
index 418ba74..a8f77ef 100644
--- a/src/api/data/arc/data.ts
+++ b/src/api/data/arc/data.ts
@@ -16,5 +16,5 @@
 
 // 查询ArcSetting列表
 export const getPage = (params: ArcDataPageReqVO) => {
-  return request.get({ url: '/data/da/arc/dataPage', params })
+  return request.get({ url: '/data/arc/data/page', params })
 }
diff --git a/src/api/data/arc/index.ts b/src/api/data/arc/index.ts
index 2ac6cc9..d6d369b 100644
--- a/src/api/data/arc/index.ts
+++ b/src/api/data/arc/index.ts
@@ -6,7 +6,8 @@
   type: string,
   point: string,
   calculate: string,
-  isEnable: string
+  sort: number,
+  isEnable: number
 }
 
 export interface ArcSettingPageReqVO extends PageParam {
@@ -16,25 +17,25 @@
 
 // 查询ArcSetting列表
 export const getArcSettingPage = (params: ArcSettingPageReqVO) => {
-  return request.get({ url: '/data/da/arc/page', params })
+  return request.get({ url: '/data/arc/setting/page', params })
 }
 
 // 查询ArcSetting详情
 export const getArcSetting = (id: number) => {
-  return request.get({ url: `/data/da/arc/info/${id}`})
+  return request.get({ url: `/data/arc/setting/info/${id}`})
 }
 
 // 新增ArcSetting
 export const createArcSetting = (data: ArcSettingVO) => {
-  return request.post({ url: '/data/da/arc/create', data })
+  return request.post({ url: '/data/arc/setting/create', data })
 }
 
 // 修改ArcSetting
 export const updateArcSetting = (data: ArcSettingVO) => {
-  return request.put({ url: '/data/da/arc/update', data })
+  return request.put({ url: '/data/arc/setting/update', data })
 }
 
 // 删除ArcSetting
 export const deleteArcSetting = (id: number) => {
-  return request.delete({ url: '/data/da/arc/delete?id=' + id })
+  return request.delete({ url: '/data/arc/setting/delete?id=' + id })
 }
diff --git a/src/api/data/da/point/daPointChart.ts b/src/api/data/da/point/daPointChart.ts
index 1090f88..bffedaa 100644
--- a/src/api/data/da/point/daPointChart.ts
+++ b/src/api/data/da/point/daPointChart.ts
@@ -6,11 +6,25 @@
   end?: Date,
 }
 
+export interface ApiPointPageReqVO extends PageParam {
+  pointNo?: string
+}
+
 // 查询chart列表
 export const getPointDaChart = (data: DaPointChartReqVO) => {
   return request.post({ url: '/data/api/query-points/chart', data })
 }
 
+// 查询多个测点当前值
+export const getPointsRealValue = (data: []) => {
+  return request.post({ url: '/data/api/query-points/real-value', data })
+}
+
+// 查询计算点当前值
+export const getMathPointCurrentValue = (data: ApiPointPageReqVO) => {
+  return request.post({ url: '/data/api/query-math-point/current-value', data })
+}
+
 //导出DaPointValue
 export const exportDaPointValue = (params) => {
   return request.download({ url: '/data/da/point/exportValue', params })
diff --git a/src/api/data/ind/item/item.ts b/src/api/data/ind/item/item.ts
index cd9d04e..0bb8833 100644
--- a/src/api/data/ind/item/item.ts
+++ b/src/api/data/ind/item/item.ts
@@ -27,6 +27,11 @@
   itemCategory: string
 }
 
+export type IndValueParam = {
+  itemNo: string
+  startDate: string
+  endTime: string
+}
 
 // 查询列表
 export const getItemPage = (params: PageParam) => {
@@ -64,3 +69,11 @@
     callback(new Error('请输入数字!'));
   }
 }
+
+export const getItemCurrentData = (itemNo: string) => {
+  return request.get({ url: '/data/api/query-ind/default-value?itemNo=' + itemNo})
+}
+
+export const getItemValueData = (params: IndValueParam) => {
+  return request.get({ url: '/data/ind-item-value/getList', params})
+}
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/api/model/matlab/mlModel.ts b/src/api/model/matlab/mlModel.ts
new file mode 100644
index 0000000..980d10f
--- /dev/null
+++ b/src/api/model/matlab/mlModel.ts
@@ -0,0 +1,29 @@
+import request from '@/config/axios'
+
+export const getPage = async (params: PageParam) => {
+  return await request.get({ url: '/model/matlab/model/page', params })
+}
+
+export const get = async (id: number) => {
+  return await request.get({ url: '/model/matlab/model/' + id })
+}
+
+export const create = async (data) => {
+  return await request.post({ url: '/model/matlab/model', data: data })
+}
+
+export const update = async (params) => {
+  return await request.put({ url: '/model/matlab/model', data: params })
+}
+
+export const deleteModel = async (id: number) => {
+  return await request.delete({ url: '/model/matlab/model?id=' + id })
+}
+
+export const list = (params) => {
+  return request.get({ url: '/model/matlab/model/list',  params})
+}
+
+export const test = (data) => {
+  return request.post({ url: '/model/matlab/model/test',  data})
+}
diff --git a/src/api/model/matlab/project.ts b/src/api/model/matlab/project.ts
new file mode 100644
index 0000000..225b952
--- /dev/null
+++ b/src/api/model/matlab/project.ts
@@ -0,0 +1,33 @@
+import request from '@/config/axios'
+
+export const getPage = async (params) => {
+  return await request.get({ url: '/model/matlab/project/page', params })
+}
+
+export const getProject = async (id: number) => {
+  return await request.get({ url: '/model/matlab/project/' + id })
+}
+
+export const createProject = async (data) => {
+  return await request.post({ url: '/model/matlab/project', data: data })
+}
+
+export const updateProject = async (params) => {
+  return await request.put({ url: '/model/matlab/project', data: params })
+}
+
+export const deleteProject = async (id: number) => {
+  return await request.delete({ url: '/model/matlab/project?id=' + id })
+}
+
+export const list = () => {
+  return request.get({ url: '/model/packageProject/project/list'})
+}
+
+export const getProjectModel = async (params) => {
+  return await request.get({ url: '/model/matlab/project/getProjectModel', params })
+}
+
+export const publish = async (data) => {
+  return await request.post({ url: '/model/matlab/project/publish', data })
+}
diff --git a/src/api/model/mpk/mpk.ts b/src/api/model/mpk/mpk.ts
index dc2782f..b0b880b 100644
--- a/src/api/model/mpk/mpk.ts
+++ b/src/api/model/mpk/mpk.ts
@@ -42,6 +42,10 @@
   return request.post({ url: '/model/mpk/api/test', data: params })
 }
 
+export const saveModel = (data) => {
+  return request.downloadByPost({ url: '/model/mpk/api/saveModel', data })
+}
+
 export const list = (params) => {
   return request.get({ url: '/model/mpk/file/list',  params})
 }
diff --git a/src/api/model/pre/accuracy/his.ts b/src/api/model/pre/accuracy/his.ts
new file mode 100644
index 0000000..b655dbf
--- /dev/null
+++ b/src/api/model/pre/accuracy/his.ts
@@ -0,0 +1,19 @@
+import request from '@/config/axios'
+
+export interface MmItemAccuracyHisVO {
+  id: string
+  rateId: string,
+  inDeviation: number,
+  inAccuracyRate: number,
+  outDeviation: number,
+  outAccuracyRate: number,
+}
+
+export interface MmItemAccuracyHisPageReqVO extends PageParam {
+  rateId?: string
+}
+
+// 查询MmItemAccuracyHis列表
+export const getMmItemAccuracyHisPage = (params: MmItemAccuracyHisPageReqVO) => {
+  return request.get({ url: '/model/item/accuracy-his/page', params })
+}
diff --git a/src/api/model/pre/accuracy/rate.ts b/src/api/model/pre/accuracy/rate.ts
new file mode 100644
index 0000000..692c730
--- /dev/null
+++ b/src/api/model/pre/accuracy/rate.ts
@@ -0,0 +1,50 @@
+import request from '@/config/axios'
+
+export interface MmItemAccuracyRateVO {
+  id: string
+  itemId: string,
+  outId: string,
+  sampleLength: number,
+  valueType: number,
+  beginTime: Date,
+  inDeviation: number,
+  inAccuracyRate: number,
+  outDeviation: number,
+  outAccuracyRate: number,
+  isEnable: number,
+}
+
+export interface MmItemAccuracyRatePageReqVO extends PageParam {
+  itemId?: string
+}
+
+// 查询MmItemAccuracyRate列表
+export const getMmItemAccuracyRatePage = (params: MmItemAccuracyRatePageReqVO) => {
+  return request.get({ url: '/model/item/accuracy-rate/page', params })
+}
+
+// 查询MmItemAccuracyRate详情
+export const getMmItemAccuracyRate = (id: number) => {
+  return request.get({ url: `/model/item/accuracy-rate/get/${id}`})
+}
+
+// 新增MmItemAccuracyRate
+export const createMmItemAccuracyRate = (data: MmItemAccuracyRateVO) => {
+  return request.post({ url: '/model/item/accuracy-rate/create', data })
+}
+
+// 修改MmItemAccuracyRate
+export const updateMmItemAccuracyRate = (data: MmItemAccuracyRateVO) => {
+  return request.put({ url: '/model/item/accuracy-rate/update', data })
+}
+
+// 删除MmItemAccuracyRate
+export const deleteMmItemAccuracyRate = (id: number) => {
+  return request.delete({ url: '/model/item/accuracy-rate/delete?id=' + id })
+}
+
+
+// 查询getItemAccuracyRateList详情
+export const getItemAccuracyRateList = () => {
+  return request.get({ url: `/model/item/accuracy-rate/list`})
+}
diff --git a/src/api/model/pre/alarm/config.ts b/src/api/model/pre/alarm/config.ts
new file mode 100644
index 0000000..b7deb23
--- /dev/null
+++ b/src/api/model/pre/alarm/config.ts
@@ -0,0 +1,57 @@
+import request from '@/config/axios'
+
+export interface MmPredictAlarmConfigVO {
+  id: string
+  title: string,
+  alarmObj: string,
+  itemId: string,
+  outId: string,
+  compLength: number,
+  upperLimit: number,
+  lowerLimit: number,
+  culUpper: number,
+  culLower: number,
+  unit: string,
+  coefficient: number,
+  scheduleId: string,
+  isEnable: number,
+  creator: string,
+  createTime: Date,
+  updater: string,
+  updateTime: Date,
+}
+
+export interface MmPredictAlarmConfigPageReqVO extends PageParam {
+  title?: string
+}
+
+// 查询MmPredictAlarmConfig列表
+export const getMmPredictAlarmConfigPage = (params: MmPredictAlarmConfigPageReqVO) => {
+  return request.get({ url: '/model/pre/alarm-config/page', params })
+}
+
+// 查询MmPredictAlarmConfig详情
+export const getMmPredictAlarmConfig = (id: number) => {
+  return request.get({ url: `/model/pre/alarm-config/get/${id}`})
+}
+
+// 新增MmPredictAlarmConfig
+export const createMmPredictAlarmConfig = (data: MmPredictAlarmConfigVO) => {
+  return request.post({ url: '/model/pre/alarm-config/create', data })
+}
+
+// 修改MmPredictAlarmConfig
+export const updateMmPredictAlarmConfig = (data: MmPredictAlarmConfigVO) => {
+  return request.put({ url: '/model/pre/alarm-config/update', data })
+}
+
+// 删除MmPredictAlarmConfig
+export const deleteMmPredictAlarmConfig = (id: number) => {
+  return request.delete({ url: '/model/pre/alarm-config/delete?id=' + id })
+}
+
+
+// 查询getPredictAlarmConfigList详情
+export const getPredictAlarmConfigList = () => {
+  return request.get({ url: `/model/pre/alarm-config/list`})
+}
diff --git a/src/api/model/pre/alarm/message.ts b/src/api/model/pre/alarm/message.ts
new file mode 100644
index 0000000..30318cd
--- /dev/null
+++ b/src/api/model/pre/alarm/message.ts
@@ -0,0 +1,28 @@
+import request from '@/config/axios'
+
+export interface MmPredictAlarmMessageVO {
+  id: string
+  configId: string,
+  title: string,
+  content: string,
+  alarmObj: string,
+  pointId: string,
+  itemId: string,
+  outId: string,
+  currentValue: number,
+  outTime: Date,
+  outValue: number,
+  alarmType: string,
+  alarmTime: Date,
+  createTime: Date,
+}
+
+export interface MmPredictAlarmMessagePageReqVO extends PageParam {
+  title?: string
+}
+
+// 查询MmPredictAlarmMessage列表
+export const getMmPredictAlarmMessagePage = (params: MmPredictAlarmMessagePageReqVO) => {
+  return request.get({ url: '/model/pre/alarm-message/page', params })
+}
+
diff --git a/src/api/model/pre/item/index.ts b/src/api/model/pre/item/index.ts
index 7fe814d..9a498ee 100644
--- a/src/api/model/pre/item/index.ts
+++ b/src/api/model/pre/item/index.ts
@@ -17,7 +17,10 @@
     predictphase: string,
     workchecked: number,
     unittransfactor: string,
-    saveindex: string
+    saveindex: string,
+    iscumulant: number,
+    cumuldivisor: number,
+    cumulpoint: string
   },
   dmModuleItem: {
     id: string,
@@ -103,6 +106,11 @@
   return request.get({ url: `/model/pre/item/list`, params})
 }
 
+// 查询MmItemOutput列表
+export const getMmItemOutputList = (params) => {
+  return request.get({ url: `/model/pre/item-output/list/all`, params})
+}
+
 export const updateModel = (data: any) => {
   return request.upload({ url: '/model/pre/item/upload-model', data })
 }
diff --git a/src/api/model/sche/model/index.ts b/src/api/model/sche/model/index.ts
index 0ac2b05..39a1184 100644
--- a/src/api/model/sche/model/index.ts
+++ b/src/api/model/sche/model/index.ts
@@ -4,6 +4,8 @@
 import * as PlanItemApi from '@/api/data/plan/item'
 import {CommonEnabled} from "@/utils/constants";
 import {getItemList, ItemVO} from "@/api/data/plan/item";
+import * as ItemApi from '@/api/data/ind/item/item'
+import {getPointSimpleList} from "@/api/data/da/point";
 
 export interface ScheduleModelVO {
   id: string
@@ -74,7 +76,7 @@
 export const getModelParamList = async (id) => {
 
   const dataPointList = ref([] as DataPointApi.DaPointVO)
-  dataPointList.value = await DataPointApi.getPointList({})
+  dataPointList.value = await DataPointApi.getPointSimpleList({})
   const pointList = []
   if (dataPointList.value) {
     dataPointList.value.forEach(item => {
@@ -143,10 +145,26 @@
     })
   }
 
+  // 指标数据
+  const indItemList = await ItemApi.getItemList({})
+  const indList = []
+  if (indItemList) {
+    indItemList.forEach(item => {
+      indList.push(
+        {
+          id: item.id,
+          name: item.itemName
+        }
+      )
+    })
+  }
+
   return {
     'DATAPOINT':pointList,
     'NormalItem': normalItemList,
     'MergeItem': mergeItemList,
     'PLAN': planList,
+    'IND': indList,
+    'IND_ASCII': indList,
   }
 }
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/router/modules/remaining.ts b/src/router/modules/remaining.ts
index f603ca5..c47f1a7 100644
--- a/src/router/modules/remaining.ts
+++ b/src/router/modules/remaining.ts
@@ -447,6 +447,29 @@
       }
     ]
   },
+  {
+    path: '/matlab',
+    component: Layout,
+    name: 'matlab',
+    meta: {
+      hidden: true
+    },
+    children: [
+      {
+        path: 'model/form/:id?',
+        component: () => import('@/views/model/matlab/model/MatlabModelForm.vue'),
+        name: 'MatlabModelForm',
+        meta: {
+          title: 'Matlab模型表单',
+          noCache: true,
+          hidden: true,
+          canTo: true,
+          icon: '',
+          activeMenu: '/matlab/model'
+        }
+      }
+    ]
+  },
 ]
 
 export default remainingRouter
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/utils/dict.ts b/src/utils/dict.ts
index ce15a93..053411e 100644
--- a/src/utils/dict.ts
+++ b/src/utils/dict.ts
@@ -166,6 +166,8 @@
   PRED_GRANULARITY = 'pred_granularity',
   ITEM_RUN_STATUS = 'item_run_status',
   RESULT_TYPE = 'result_type',
+  MATLAB_PLATFORM= 'matlab_platform',
+  MATLAB_VERSION= 'matlab_version',
   // ========== DATA - 数据平台模块  ==========
   DATA_FIELD_TYPE = 'data_field_type',
   TAG_DATA_TYPE = 'tag_data_type',
@@ -189,5 +191,6 @@
   MODEL_RESULT_TYPE = 'model_result_type',
   DATA_QUALITY = 'data_quality',
   ARC_TYPE = 'arc_type',
-  ARC_CALCULATE_TYPE = 'arc_calculate_type'
+  ARC_CALCULATE_TYPE = 'arc_calculate_type',
+  SOLIDIFY_FLAG = 'ind_solidify_flag'
 }
diff --git a/src/utils/download.ts b/src/utils/download.ts
index 1d07484..943374b 100644
--- a/src/utils/download.ts
+++ b/src/utils/download.ts
@@ -50,7 +50,10 @@
       a.download = 'image.png'
       a.click()
     }
-  }
+  },
+  downloadFile: (data: Blob, fileName: string) => {
+    download0(data, fileName, 'application/octet-stream')
+  },
 }
 
 export default download
diff --git a/src/views/data/arc/ArcData.vue b/src/views/data/arc/ArcData.vue
index 3e769da..31400a9 100644
--- a/src/views/data/arc/ArcData.vue
+++ b/src/views/data/arc/ArcData.vue
@@ -1,7 +1,7 @@
 <template>
   <el-drawer
     v-model="drawer"
-    size="60%"
+    size="50%"
     title="归档数据"
     :direction="direction"
     :before-close="handleClose"
@@ -21,7 +21,7 @@
             format="YYYY-MM-DD HH:mm:00"
             value-format="YYYY-MM-DD HH:mm:00"
             type="datetime"
-            :clearable="false"
+            :clearable="true"
             placeholder="选择日期时间"/>
         </el-form-item>
         <el-form-item label="结束时间">
@@ -30,7 +30,7 @@
             format="YYYY-MM-DD HH:mm:00"
             value-format="YYYY-MM-DD HH:mm:00"
             type="datetime"
-            :clearable="false"
+            :clearable="true"
             placeholder="选择日期时间"/>
         </el-form-item>
         <el-form-item>
@@ -42,7 +42,7 @@
     <ContentWrap>
       <el-table v-loading="loading" :data="list">
         <el-table-column
-          prop="value"
+          prop="arcValue"
           label="数据值"
           header-align="center"
           align="center"
@@ -93,7 +93,7 @@
     pageSize: 10,
     arcId:undefined,
     startTime: undefined,
-    endTime: getYMDHM0(new Date()),
+    endTime: undefined
   })
   const queryFormRef = ref() // 搜索的表单
   const exportLoading = ref(false) // 导出的加载中
@@ -139,7 +139,7 @@
     queryParams.pageSize = 10
     queryParams.arcId = ''
     queryParams.startTime = ''
-    queryParams.endTime = getYMDHM0(new Date())
+    queryParams.endTime = ''
   }
 
   const handleClose = (done: () => void) => {
diff --git a/src/views/data/arc/ArcSettingForm.vue b/src/views/data/arc/ArcSettingForm.vue
index 740e1f1..bd5293f 100644
--- a/src/views/data/arc/ArcSettingForm.vue
+++ b/src/views/data/arc/ArcSettingForm.vue
@@ -7,24 +7,55 @@
       :rules="formRules"
       label-width="120px"
     >
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="formData.name" placeholder="请输入归档名称" />
-      </el-form-item>
-      <el-form-item label="归档周期" prop="type">
-        <el-select
-          v-model="formData.type"
-          clearable
-          placeholder="请选择归档周期"
-        >
-          <el-option
-            v-for="dict in getDictOptions(DICT_TYPE.ARC_TYPE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="归档点位" prop="point">
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="编码" prop="code">
+            <el-input v-model="formData.code" placeholder="请输入编码" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="名称" prop="name">
+            <el-input v-model="formData.name" placeholder="请输入名称" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="归档周期" prop="type">
+            <el-select
+              v-model="formData.type"
+              clearable
+              placeholder="请选择归档周期"
+            >
+              <el-option
+                v-for="dict in getDictOptions(DICT_TYPE.ARC_TYPE)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="计算方法" prop="calculate">
+            <el-select
+              v-model="formData.calculate"
+              clearable
+              placeholder="请选择计算方法"
+            >
+              <el-option
+                v-for="dict in getDictOptions(DICT_TYPE.ARC_CALCULATE_TYPE)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="归档点位" prop="point">
             <el-select
               v-model="formData.point"
               filterable
@@ -35,35 +66,25 @@
                 :label="item.pointName"
                 :value="item.pointNo"/>
             </el-select>
-      </el-form-item>
-      <el-form-item label="计算方法" prop="calculate">
-        <el-select
-          v-model="formData.calculate"
-          clearable
-          placeholder="请选择计算方法"
-        >
-          <el-option
-            v-for="dict in getDictOptions(DICT_TYPE.ARC_CALCULATE_TYPE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="是否启用" prop="isEnable">
-        <el-select
-          v-model="formData.isEnable"
-          clearable
-          placeholder="请选择是否启用"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="是否启用" prop="isEnable">
+            <el-select
+              v-model="formData.isEnable"
+              clearable
+              placeholder="请选择是否启用"
+            >
+              <el-option
+                v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
     </el-form>
     <template #footer>
       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
@@ -87,13 +108,16 @@
   const formType = ref('') // 表单的类型:create - 新增;update - 修改
   const formData = ref({
     id: undefined,
+    code: undefined,
     name: undefined,
     type: undefined,
     point: undefined,
     calculate: undefined,
+    sort: 1,
     isEnable: 1
   })
   const formRules = reactive({
+    code: [{ required: true, message: '编码不能为空', trigger: 'blur' }],
     name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
     type: [{ required: true, message: '归档周期不能为空', trigger: 'blur' }],
     point: [{ required: true, message: '归档点位不能为空', trigger: 'blur' }],
@@ -162,6 +186,7 @@
       type: undefined,
       point: undefined,
       calculate: undefined,
+      sort: 1,
       isEnable: 1
     }
     formRef.value?.resetFields()
diff --git a/src/views/data/arc/index.vue b/src/views/data/arc/index.vue
index 922ae9e..637097c 100644
--- a/src/views/data/arc/index.vue
+++ b/src/views/data/arc/index.vue
@@ -1,167 +1,176 @@
 <template>
-    <!-- 搜索 -->
-    <ContentWrap>
-        <el-form
-                class="-mb-15px"
-                :model="queryParams"
-                ref="queryFormRef"
-                :inline="true"
-                label-width="68px"
-        >
-            <el-form-item label="名称" prop="name">
-                <el-input
-                        v-model="queryParams.name"
-                        placeholder="请输入名称"
-                        clearable
-                        @keyup.enter="handleQuery"
-                        class="!w-240px"
-                />
-            </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')"
-                >
-                    <Icon icon="ep:plus" class="mr-5px"/>
-                    新增
-                </el-button>
-            </el-form-item>
-        </el-form>
-    </ContentWrap>
-
-    <!-- 列表 -->
-    <ContentWrap>
-        <el-table v-loading="loading" :data="list">
-            <el-table-column label="名称" align="center" prop="name"/>
-            <el-table-column label="归档周期" align="center" prop="type"/>
-            <el-table-column label="归档点位" align="center" prop="point"/>
-            <el-table-column label="计算方法" align="center" prop="calculate"/>
-            <el-table-column label="是否启用" align="center" prop="isEnable"/>
-            <el-table-column label="操作" align="center" min-width="110" fixed="right">
-                <template #default="scope">
-                    <el-button
-                            link
-                            type="primary"
-                            @click="openForm('update', scope.row.id)"
-                    >
-                        编辑
-                    </el-button>
-                    <el-button
-                            link
-                            type="primary"
-                            @click="openArcData(scope.row.id)"
-                    >
-                        历史值
-                    </el-button>
-                    <el-button
-                            link
-                            type="danger"
-                            @click="handleDelete(scope.row.id)"
-                    >
-                        删除
-                    </el-button>
-                </template>
-            </el-table-column>
-        </el-table>
-        <!-- 分页 -->
-        <Pagination
-                :total="total"
-                v-model:page="queryParams.pageNo"
-                v-model:limit="queryParams.pageSize"
-                @pagination="getList"
+  <!-- 搜索 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入名称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
         />
-    </ContentWrap>
+      </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="['data:arc:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px"/>
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
 
-    <!-- 表单弹窗:添加/修改 -->
-    <ArcSettingForm ref="formRef" @success="getList"/>
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编码" align="center" prop="code"/>
+      <el-table-column label="名称" align="center" prop="name"/>
+      <el-table-column label="归档周期" align="center" prop="type"/>
+      <el-table-column label="归档点位" align="center" prop="point"/>
+      <el-table-column label="计算方法" align="center" prop="calculate"/>
+      <el-table-column label="是否启用" align="center" prop="isEnable">
+        <template #default="scope">
+          <el-tag v-if="scope.row.isEnable === 1" size="small">是</el-tag>
+          <el-tag v-else size="small" type="danger">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" min-width="110" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['data:arc:update']"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            @click="openArcData(scope.row.id)"
+          >
+            历史值
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['data:arc: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>
 
-    <!-- 历史值弹窗 -->
-    <ArcData ref="dataRef"/>
+  <!-- 表单弹窗:添加/修改 -->
+  <ArcSettingForm ref="formRef" @success="getList"/>
+
+  <!-- 历史值弹窗 -->
+  <ArcData ref="dataRef"/>
 
 </template>
 <script lang="ts" setup>
-    import * as ArcSetting from '@/api/data/arc/index'
-    import ArcSettingForm from './ArcSettingForm.vue'
-    import ArcData from './ArcData.vue'
+import * as ArcSetting from '@/api/data/arc/index'
+import ArcSettingForm from './ArcSettingForm.vue'
+import ArcData from './ArcData.vue'
 
-    defineOptions({name: 'DataArc'})
+defineOptions({name: 'DataArc'})
 
-    const message = useMessage() // 消息弹窗
-    const {t} = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+const {t} = useI18n() // 国际化
 
-    const loading = ref(true) // 列表的加载中
-    const total = ref(0) // 列表的总页数
-    const list = ref([]) // 列表的数据
-    const queryParams = reactive({
-        pageNo: 1,
-        pageSize: 10,
-        name: undefined,
-        type: undefined
-    })
-    const queryFormRef = ref() // 搜索的表单
-    const exportLoading = ref(false) // 导出的加载中
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: undefined,
+  type: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
 
-    /** 查询列表 */
-    const getList = async () => {
-        loading.value = true
-        try {
-            const page = await ArcSetting.getArcSettingPage(queryParams)
-            list.value = page.list
-            total.value = page.total
-        } finally {
-            loading.value = false
-        }
-    }
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const page = await ArcSetting.getArcSettingPage(queryParams)
+    list.value = page.list
+    total.value = page.total
+  } finally {
+    loading.value = false
+  }
+}
 
-    /** 搜索按钮操作 */
-    const handleQuery = () => {
-        queryParams.pageNo = 1
-        getList()
-    }
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
 
-    /** 重置按钮操作 */
-    const resetQuery = () => {
-        queryFormRef.value.resetFields()
-        handleQuery()
-    }
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
 
-    /** 添加/修改操作 */
-    const formRef = ref()
-    const openForm = (type: string, id?: number) => {
-        formRef.value.open(type, id)
-    }
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
 
-    /** 历史操作 */
-    const dataRef = ref()
-    const openArcData = (id?: string) => {
-      dataRef.value.open(id)
-    }
+/** 历史操作 */
+const dataRef = ref()
+const openArcData = (id?: string) => {
+  dataRef.value.open(id)
+}
 
-    /** 删除按钮操作 */
-    const handleDelete = async (id: number) => {
-        try {
-            // 删除的二次确认
-            await message.delConfirm()
-            // 发起删除
-            await ArcSetting.deleteArcSetting(id)
-            message.success(t('common.delSuccess'))
-            // 刷新列表
-            await getList()
-        } catch {
-        }
-    }
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await ArcSetting.deleteArcSetting(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {
+  }
+}
 
-    /** 初始化 **/
-    onMounted(async () => {
-        await getList()
-    })
+/** 初始化 **/
+onMounted(async () => {
+  await getList()
+})
 </script>
diff --git a/src/views/data/ind/data/DataSetForm.vue b/src/views/data/ind/data/DataSetForm.vue
index 132e25c..d09745b 100644
--- a/src/views/data/ind/data/DataSetForm.vue
+++ b/src/views/data/ind/data/DataSetForm.vue
@@ -21,8 +21,13 @@
         </el-select>
       </el-form-item>
       <el-form-item label="查询语句" prop="querySql">
-        <el-input v-model="formData.querySql" placeholder="请输入内容" type="textarea" maxlength="200"
+        <el-input v-model="formData.querySql" placeholder="请输入内容" type="textarea" maxlength="500"
+                  :rows="6"
+                  @input="checkSensitiveWords"
                   show-word-limit spellcheck="false"/>
+      </el-form-item>
+      <el-form-item v-if="showError">
+        <p>输入中包含以下敏感词:<span style="color: red">{{sensitiveMessage}}</span></p>
       </el-form-item>
       <el-form-item label="备注" prop="remark">
         <el-input v-model="formData.remark" placeholder="请输入内容" type="textarea" maxlength="100"
@@ -30,25 +35,29 @@
       </el-form-item>
     </el-form>
     <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button :disabled="disableSubmit" type="primary" @click="submitForm">确 定</el-button>
       <el-button @click="dialogVisible = false">取 消</el-button>
     </template>
   </Dialog>
 </template>
 <script lang="ts" setup>
-  import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-  import * as DataSetApi from '@/api/data/ind/data/data.set'
-  import { CommonStatusEnum } from '@/utils/constants'
-  import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
+import * as DataSetApi from '@/api/data/ind/data/data.set'
+import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
 
-  defineOptions({ name: 'IndDataSetForm' })
+defineOptions({ name: 'IndDataSetForm' })
 
   const { t } = useI18n() // 国际化
   const message = useMessage() // 消息弹窗
 
+  const showError = ref(false)
+  const foundSensitiveWords = ref()
+  const sensitiveMessage = ref('')
+  const sensitiveWords = [';', 'master', 'truncate', 'insert', 'delete', 'update', 'declare', 'alter', 'drop']
+
   const dialogVisible = ref(false) // 弹窗的是否展示
   const dialogTitle = ref('') // 弹窗的标题
-  const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+  const formLoading = ref(false) // 表单的加载中:修改时的数据加载
+  const disableSubmit = ref(false) // 禁止提交
   const formType = ref('') // 表单的类型:create - 新增;update - 修改
   const formData = ref({
     id: undefined,
@@ -84,10 +93,26 @@
         formLoading.value = false
       }
     }
-
-
   }
   defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+  /**
+   * 验证敏感词
+   */
+  const checkSensitiveWords = () => {
+    showError.value = false;
+    const regex = new RegExp(sensitiveWords.map(word => `${word}`).join('|'), 'gi');
+    let matches = formData.value.querySql.match(regex);
+    if (matches) {
+      showError.value = true;
+      foundSensitiveWords.value = Array.from(new Set(matches));
+      disableSubmit.value = true
+      sensitiveMessage.value = foundSensitiveWords.value.join('、')
+    } else {
+      foundSensitiveWords.value = undefined
+      disableSubmit.value = false
+    }
+  }
 
   /** 提交表单 */
   const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
@@ -98,6 +123,7 @@
     if (!valid) return
     // 提交请求
     formLoading.value = true
+    disableSubmit.value = true
     try {
       const data = formData.value as DataSetApi.DataSetVO
       if (formType.value === 'create') {
@@ -112,6 +138,7 @@
       emit('success')
     } finally {
       formLoading.value = false
+      disableSubmit.value = false
     }
   }
 
diff --git a/src/views/data/ind/item/AtomIndDefineForm.vue b/src/views/data/ind/item/AtomIndDefineForm.vue
index 00ab2f5..6368c02 100644
--- a/src/views/data/ind/item/AtomIndDefineForm.vue
+++ b/src/views/data/ind/item/AtomIndDefineForm.vue
@@ -20,19 +20,19 @@
       <el-row>
         <el-col :span="12">
           <el-form-item label="指标分类" prop="itemCategory">
-            <el-select v-model="formData.itemCategory" clearable placeholder="请选择指标分类">
-              <el-option
-                v-for="item in dataCategoryList"
-                :key="item.id"
-                :label="item.label"
-                :value="item.id + ''"
-              />
-            </el-select>
+            <el-tree-select
+              v-model="formData.itemCategory"
+              :data="dataCategoryList"
+              :default-expanded-keys="[0]"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="时间粒度" prop="timeGranularity">
-            <el-select v-model="formData.timeGranularity" placeholder="请选择">
+            <el-select v-model="formData.timeGranularity" clearable placeholder="请选择">
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.TIME_GRANULARITY)"
                 :key="dict.value"
@@ -59,11 +59,28 @@
             <el-input v-model="formData.unit"/>
           </el-form-item>
         </el-col>
+        <el-col :span="6">
+          <el-form-item label="固化标识" prop="solidifyFlag">
+            <el-select v-model="formData.solidifyFlag"
+                       clearable
+                       filterable
+                       allow-create
+                       placeholder="请选择">
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.SOLIDIFY_FLAG)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
       </el-row>
       <el-row>
-        <el-col :span="6">
+        <el-col :span="12">
           <el-form-item label="数据集" prop="atomItem.dataSet">
-            <el-select v-model="formData.atomItem.dataSet" clearable placeholder="请选择数据集" @change="handleDataSetChange($event)">
+            <el-select v-model="formData.atomItem.dataSet" filterable
+                       allow-create clearable placeholder="请选择数据集" @change="handleDataSetChange($event)">
               <el-option
                 v-for="item in dataSetList"
                 :key="item.id"
@@ -75,7 +92,8 @@
         </el-col>
         <el-col :span="6">
           <el-form-item label="使用字段" prop="atomItem.usingField">
-            <el-select v-model="formData.atomItem.usingField" clearable placeholder="请选择字段">
+            <el-select v-model="formData.atomItem.usingField" filterable
+                       allow-create clearable placeholder="请选择字段">
               <el-option
                 v-for="item in dataSetFieldList"
                 :key="item.id"
@@ -86,7 +104,7 @@
           </el-form-item>
         </el-col>
         <el-col :span="6">
-          <el-form-item label="统计方式" prop="statFunc">
+          <el-form-item label="统计方式" prop="atomItem.statFunc">
             <el-select v-model="formData.atomItem.statFunc" clearable placeholder="请选择">
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.DATA_STAT_FUNC)"
@@ -118,7 +136,7 @@
   import * as DataSetApi from '@/api/data/ind/data/data.set'
   import * as DataSetFieldApi from '@/api/data/ind/data/data.field'
   import * as CategoryApi from '@/api/data/ind/category/index'
-
+  import {handleTree} from "@/utils/tree";
 
   defineOptions({name: 'IndDataSetForm'})
 
@@ -141,6 +159,7 @@
     timeRange: '',
     timeGranularity: '',
     remark: '',
+    solidifyFlag: '',
     atomItem:{
       dataSource:'',
       dataSet: '',
@@ -157,17 +176,22 @@
   const formRules = reactive({
     itemName: [{required: true, message: '指标名称不能为空', trigger: 'blur'}],
     itemCategory: [{required: true, message: '指标分类不能为空', trigger: 'blur'}],
-    /*precision: [{validator: validateAsNumber, trigger: 'blur' }],
-    coefficient: [{validator: validateAsNumber, trigger: 'blur' }],
-    statFunc: [{required: true, message: '统计方式不能为空', trigger: 'blur'}],
-    timeGranularity: [{required: true, message: '时间粒度不能为空', trigger: 'blur'}],
-    "atomItem.dataSet": [{required: true, message: '数据集不能为空', trigger: 'blur'}],
-    "atomItem.usingField":[{required: true, message: '使用字段不能为空', trigger: 'blur'}]*/
+    "atomItem.usingField": [{required: true, message: '使用字段不能为空', trigger: 'blur'}],
+    // "atomItem.statFunc": [{required: true, message: '统计方式不能为空', trigger: 'blur'}],
   })
   const formRef = ref() // 表单 Ref
   const dataSetList = ref([] as DataSetApi.DataSetVO[])
   const dataSetFieldList = ref([] as DataSetFieldApi.DataSetFieldVO[])
-  const dataCategoryList = ref([])
+
+  const dataCategoryList = ref<Tree[]>([])
+
+  const getCategoryTree = async () => {
+    dataCategoryList.value = []
+    const res = await CategoryApi.getCategoryListAllSimple()
+    let category: Tree = {id: 0, label: '主类目', children: []}
+    category.children = handleTree(res, 'id', 'pid')
+    dataCategoryList.value.push(category)
+  }
   /** 打开弹窗 */
   const open = async (type: string, id?: string) => {
     dialogVisible.value = true
@@ -176,7 +200,7 @@
     resetForm()
     // 加载数据源列表
     dataSetList.value = await DataSetApi.getDataSetList()
-    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
+    await getCategoryTree()
     // 修改时,设置数据
     if (id) {
       formLoading.value = true
diff --git a/src/views/data/ind/item/CalIndDefineForm.vue b/src/views/data/ind/item/CalIndDefineForm.vue
index 0d5fc81..28a1235 100644
--- a/src/views/data/ind/item/CalIndDefineForm.vue
+++ b/src/views/data/ind/item/CalIndDefineForm.vue
@@ -20,14 +20,14 @@
       <el-row>
         <el-col :span="12">
           <el-form-item label="指标分类" prop="itemCategory">
-            <el-select v-model="formData.itemCategory" clearable placeholder="请选择指标分类">
-              <el-option
-                v-for="item in dataCategoryList"
-                :key="item.id"
-                :label="item.label"
-                :value="item.id + ''"
-              />
-            </el-select>
+            <el-tree-select
+              v-model="formData.itemCategory"
+              :data="dataCategoryList"
+              :default-expanded-keys="[0]"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
@@ -44,19 +44,35 @@
         </el-col>
       </el-row>
       <el-row>
-        <el-col :span="8">
+        <el-col :span="6">
           <el-form-item label="指标精度" prop="precision">
             <el-input v-model="formData.precision"/>
           </el-form-item>
         </el-col>
-        <el-col :span="8">
+        <el-col :span="6">
           <el-form-item label="转换系数" prop="coefficient">
             <el-input v-model="formData.coefficient"/>
           </el-form-item>
         </el-col>
-        <el-col :span="8">
+        <el-col :span="6">
           <el-form-item label="数量单位" prop="unit">
             <el-input v-model="formData.unit"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="固化标识" prop="solidifyFlag">
+            <el-select v-model="formData.solidifyFlag"
+                       clearable
+                       filterable
+                       allow-create
+                       placeholder="请选择">
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.SOLIDIFY_FLAG)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
           </el-form-item>
         </el-col>
       </el-row>
@@ -139,6 +155,7 @@
   import * as ItemApi from '@/api/data/ind/item/item'
   import { ElMessage } from 'element-plus'
   import * as CategoryApi from '@/api/data/ind/category/index'
+  import {handleTree} from "@/utils/tree";
 
   defineOptions({name: 'IndDataSetForm'})
 
@@ -162,6 +179,7 @@
     timeRange: '',
     timeGranularity: '',
     remark: '',
+    solidifyFlag:'',
     calItem: {
       id: '',
       expression: '',
@@ -183,14 +201,22 @@
   const operatorList = ref(['+', '-', '*', '/', '&', '|', '!', '>', '<'])
   const formRules = reactive({
     itemName: [{required: true, message: '指标名称不能为空', trigger: 'blur'}],
-    itemCategory: [{required: true, message: '指标类型不能为空', trigger: 'blur'}],
-    precision: [{validator: validateAsNumber, trigger: 'blur' }],
-    coefficient: [{validator: validateAsNumber, trigger: 'blur' }],
+    itemCategory: [{required: true, message: '指标类型不能为空', trigger: 'blur'}]
+    // precision: [{validator: validateAsNumber, trigger: 'blur' }],
+    // coefficient: [{validator: validateAsNumber, trigger: 'blur' }],
   })
   const formRef = ref() // 表单 Ref
   const dataSourceList = ref([] as DataSourceConfigApi.DataSourceConfigVO[])
   const queryParams = reactive({})
-  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
+
+  const dataCategoryList = ref<Tree[]>([])
+  const getCategoryTree = async () => {
+    dataCategoryList.value = []
+    const res = await CategoryApi.getCategoryListAllSimple()
+    let category: Tree = {id: 0, label: '主类目', children: []}
+    category.children = handleTree(res, 'id', 'pid')
+    dataCategoryList.value.push(category)
+  }
   /** 打开弹窗 */
   const open = async (type: string, id?: number) => {
     dialogVisible.value = true
@@ -199,7 +225,7 @@
     resetForm()
 
     // 加载数据源列表
-    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
+    await getCategoryTree()
     itemList.value = await ItemApi.getItemList(queryParams)
     // 修改时,设置数据
     if (id) {
diff --git a/src/views/data/ind/item/DerIndDefineForm.vue b/src/views/data/ind/item/DerIndDefineForm.vue
index 3822e86..eccf7c0 100644
--- a/src/views/data/ind/item/DerIndDefineForm.vue
+++ b/src/views/data/ind/item/DerIndDefineForm.vue
@@ -7,8 +7,21 @@
       :rules="formRules" label-width="100px">
       <el-row>
         <el-col :span="12">
+          <el-form-item label="指标编码" prop="itemNo">
+            <el-input v-model="formData.itemNo" disabled/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="指标名称" prop="itemName">
+            <el-input v-model="formData.itemName"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
           <el-form-item label="原子指标" prop="atomItem.itemId">
-            <el-select v-model="formData.atomItem.itemId" clearable placeholder="请选择原子指标"
+            <el-select v-model="formData.atomItem.itemId" filterable
+                       allow-create clearable placeholder="请选择原子指标"
                        @change="handleChange($event)">
               <el-option
                 v-for="item in atomItemList"
@@ -27,27 +40,15 @@
       </el-row>
       <el-row>
         <el-col :span="12">
-          <el-form-item label="指标编码" prop="itemNo">
-            <el-input v-model="formData.itemNo" disabled/>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="指标名称" prop="itemName">
-            <el-input v-model="formData.itemName"/>
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row>
-        <el-col :span="12">
           <el-form-item label="指标分类" prop="itemCategory">
-            <el-select v-model="formData.itemCategory" clearable placeholder="请选择指标分类">
-              <el-option
-                v-for="item in dataCategoryList"
-                :key="item.id"
-                :label="item.label"
-                :value="item.id + ''"
-              />
-            </el-select>
+            <el-tree-select
+              v-model="formData.itemCategory"
+              :data="dataCategoryList"
+              :default-expanded-keys="[0]"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
@@ -79,11 +80,27 @@
             <el-input v-model="formData.unit"/>
           </el-form-item>
         </el-col>
+        <el-col :span="6">
+          <el-form-item label="固化标识" prop="solidifyFlag">
+            <el-select v-model="formData.solidifyFlag"
+                       clearable
+                       filterable
+                       allow-create
+                       placeholder="请选择">
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.SOLIDIFY_FLAG)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
       </el-row>
       <el-row>
         <el-col :span="6">
           <el-form-item label="时间标识" prop="timeLabel">
-            <el-select v-model="formData.derItem.timeLabel" clearable placeholder="请选择时间标识">
+            <el-select v-model="formData.derItem.timeLabel" allow-create filterable clearable placeholder="请选择时间标识">
               <el-option
                 v-for="item in dataSetFieldList"
                 :key="item.id"
@@ -130,7 +147,8 @@
       <el-row>
         <el-col :span="24">
           <el-form-item label="分析维度" prop="dimension">
-            <el-select v-model="formData.derItem.dimension" clearable placeholder="请选择分析维度" multiple>
+            <el-select v-model="formData.derItem.dimension" filterable
+                       allow-create clearable placeholder="请选择分析维度" multiple>
               <el-option
                 v-for="item in dataSetFieldList"
                 :key="item.id"
@@ -164,6 +182,7 @@
   import {PageParam} from "@/api/data/ind/item/item";
   import * as CategoryApi from "@/api/data/ind/category";
   import * as DataSetFieldApi from "@/api/data/ind/data/data.field";
+  import {handleTree} from "@/utils/tree";
 
   defineOptions({name: 'IndDataSetForm'})
 
@@ -185,6 +204,7 @@
     businessType: '',
     timeRange: '',
     timeGranularity: '',
+    solidifyFlag:'',
     atomItem: {
       id: '',
       itemId: '',
@@ -218,9 +238,16 @@
   const formRef = ref() // 表单 Ref
   const atomItemList = ref([] as ItemApi.ItemVO[])
   const showTimeChange = ref(false)
-  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
   const dataSetFieldList = ref([] as DataSetFieldApi.DataSetFieldVO[])
+  const dataCategoryList = ref<Tree[]>([])
 
+  const getCategoryTree = async () => {
+    dataCategoryList.value = []
+    const res = await CategoryApi.getCategoryListAllSimple()
+    let category: Tree = {id: 0, label: '主类目', children: []}
+    category.children = handleTree(res, 'id', 'pid')
+    dataCategoryList.value.push(category)
+  }
   /** 打开弹窗 */
   const open = async (type: string, id?: string) => {
     dialogVisible.value = true
@@ -228,7 +255,7 @@
     formType.value = type
     resetForm()
     // 加载数据源列表
-    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
+    await getCategoryTree()
     const queryParams = reactive({
       itemType: 'ATOM'
     })
diff --git a/src/views/data/ind/item/IndCurrentData.vue b/src/views/data/ind/item/IndCurrentData.vue
new file mode 100644
index 0000000..624ca10
--- /dev/null
+++ b/src/views/data/ind/item/IndCurrentData.vue
@@ -0,0 +1,45 @@
+<template>
+  <el-dialog
+    title="指标当前值"
+    :close-on-click-modal="false"
+    width="30%"
+    v-model="visible"
+  >
+    <el-form
+      :inline="true"
+      :model="dataForm"
+    >
+      <el-form-item>
+        <el-button @click="getData()">查询</el-button>
+      </el-form-item>
+      <el-form-item>
+        <el-input v-model="dataForm.itemCurrentData" type="textarea" :rows="15" style="width: 550px" disabled/>
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import {ref} from 'vue';
+import * as ItemApi from '@/api/data/ind/item/item'
+
+const message = useMessage() // 消息弹窗
+const visible = ref(false);
+const dataForm = ref({
+  itemNo: "",
+  itemCurrentData: "",
+});
+
+/** 打开弹窗 */
+const open = async (itemNo: string) => {
+  visible.value = true
+  dataForm.value.itemNo = itemNo
+  dataForm.value.itemCurrentData = JSON.stringify(await ItemApi.getItemCurrentData(itemNo));
+}
+
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+const getData = async() =>{
+  dataForm.value.itemCurrentData = JSON.stringify(await ItemApi.getItemCurrentData(dataForm.value.itemNo));
+}
+</script>
diff --git a/src/views/data/ind/item/IndHistoryChart.vue b/src/views/data/ind/item/IndHistoryChart.vue
new file mode 100644
index 0000000..e41ec10
--- /dev/null
+++ b/src/views/data/ind/item/IndHistoryChart.vue
@@ -0,0 +1,169 @@
+<template>
+  <el-dialog
+    title="历史值"
+    :close-on-click-modal="false"
+    width="50%"
+    v-model="visible"
+  >
+    <el-form
+      :inline="true"
+      :model="dataForm"
+      @keydown.enter="getDataList()"
+    >
+      <el-form-item label="开始时间">
+        <el-date-picker
+          size="mini"
+          v-model="dataForm.startTime"
+          format="YYYY-MM-DD HH:mm:00"
+          value-format="YYYY-MM-DD HH:mm:00"
+          type="datetime"
+          :clearable="false"
+          placeholder="选择日期时间"/>
+      </el-form-item>
+      <el-form-item label="结束时间">
+        <el-date-picker
+          size="mini"
+          v-model="dataForm.endTime"
+          format="YYYY-MM-DD HH:mm:00"
+          value-format="YYYY-MM-DD HH:mm:00"
+          type="datetime"
+          :clearable="false"
+          placeholder="选择日期时间"/>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="getDataList()">查询</el-button>
+      </el-form-item>
+    </el-form>
+    <div ref="chartDom" class="result-chart" v-loading="loading"></div>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import {ref} from 'vue';
+import * as echarts from 'echarts';
+import * as ItemApi from '@/api/data/ind/item/item'
+import {getYMDHM0} from "@/utils/dateUtil"
+import download from "@/utils/download";
+
+const message = useMessage() // 消息弹窗
+const visible = ref(false);
+const chartDom = ref(null);
+let myChart = null;
+const chartParams = reactive({
+  itemNo: undefined,
+  startTime: undefined,
+  endTime: undefined,
+})
+const dataForm = ref({
+  id: "",
+  itemNo: "",
+  itemName: "",
+  startTime: getYMDHM0(new Date() - 60 * 60 * 1000),
+  endTime: "",
+});
+
+/** 打开弹窗 */
+const open = async (row: object) => {
+  visible.value = true
+  dataForm.value.id = row.id;
+  dataForm.value.itemNo = row.itemNo;
+  dataForm.value.itemName = row.itemName;
+  getDataList();
+}
+
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+const loading = ref(false)
+
+async function getDataList() {
+  visible.value = true;
+  loading.value = true
+  if (dataForm.value.id) {
+    try {
+      chartParams.itemNo = dataForm.value.itemNo;
+      chartParams.startTime = dataForm.value.startTime;
+      chartParams.endTime = dataForm.value.endTime;
+      const result = await ItemApi.getItemValueData(chartParams)
+      loading.value = false
+      let xData = result.map(obj => obj['dataTime']);
+      let yData = result.map(obj => obj['dataValue']);
+      let data = xData.map((x, index) => [x, yData[index]]);
+      let seriesData = []
+      seriesData.push({
+        name: dataForm.value.itemName,
+        type: "line",
+        data: data,
+        showSymbol: true,
+        smooth: false,
+        lineStyle: {
+          normal: {
+            color: "#5B8FF9",
+            width: 1,
+          },
+        },
+      });
+
+      myChart = echarts.init(chartDom.value);
+      const option = {
+        title: {
+          text: dataForm.value.itemName,
+          top: 0,
+          left: "1%",
+          textStyle: {
+            fontSize: 14,
+          },
+        },
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            type: "line",
+            lineStyle: {
+              color: "#cccccc",
+              width: "1",
+              type: "dashed",
+            },
+          },
+        },
+        legend: {
+          show: false,
+          top: 10,
+        },
+        grid: {
+          top: 30,
+          left: "3%",
+          right: "5%",
+          bottom: 10,
+          containLabel: true,
+        },
+        xAxis: {
+          type: "category",
+          boundaryGap: false,
+          data: xData,
+        },
+        yAxis: {
+          type: "value",
+        },
+        dataZoom: [
+          {
+            type: "inside",
+          },
+        ],
+        series: seriesData,
+      };
+      myChart.setOption(option);
+    } catch (error) {
+      console.error(error)
+    }
+  }
+}
+
+</script>
+<style>
+.el-select {
+  width: 100%;
+}
+
+.result-chart {
+  height: 500px;
+}
+</style>
diff --git a/src/views/data/ind/item/index.vue b/src/views/data/ind/item/index.vue
index b56ed54..3a3c9a9 100644
--- a/src/views/data/ind/item/index.vue
+++ b/src/views/data/ind/item/index.vue
@@ -1,105 +1,136 @@
 <template>
-  <!-- 搜索工作栏 -->
-  <ContentWrap>
-    <el-form ref="queryFormRef" :inline="true" :model="queryParams" class="-mb-15px"
-             label-width="68px">
-      <el-form-item label="指标编码" prop="itemNo">
-        <el-input v-model="queryParams.itemNo" class="!w-200px" clearable placeholder="请输入指标编码"
-                  @keyup.enter="handleQuery"/>
-      </el-form-item>
-      <el-form-item label="指标名称" prop="itemName">
-        <el-input v-model="queryParams.itemName" class="!w-200px" clearable placeholder="请输入指标名称"
-                  @keyup.enter="handleQuery"/>
-      </el-form-item>
-      <el-form-item label="指标类型" prop="itemType">
-        <el-select v-model="queryParams.itemType"  class="!w-200px" clearable placeholder="请选择指标类型">
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.IND_ITEM_TYPE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery">
-          <Icon class="mr-5px" icon="ep:search"/>
-          搜索
-        </el-button>
-        <el-button @click="resetQuery">
-          <Icon class="mr-5px" icon="ep:refresh"/>
-          重置
-        </el-button>
-        <el-button
-          v-hasPermi="['data:ind-item:create']"
-          plain
-          type="primary"
-          @click="openForm('create')"
-        >
-          <Icon class="mr-5px" icon="ep:plus"/>
-          新增
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
+  <el-row :gutter="20">
+    <el-col :span="3" :xs="24">
+      <ContentWrap class="h-1/1">
+        <el-tree
+          style="max-width: 600px"
+          :data="dataCategoryList"
+          :props="treeProps"
+          default-expand-all
+          highlight-current
+          @node-click="handleNodeClick"
+        />
+      </ContentWrap>
+    </el-col>
+    <el-col :span="21" :xs="24">
+      <!-- 搜索工作栏 -->
+      <ContentWrap>
+        <el-form ref="queryFormRef" :inline="true" :model="queryParams" class="-mb-15px"
+                 label-width="68px">
+          <el-form-item label="指标编码" prop="itemNo">
+            <el-input v-model="queryParams.itemNo" class="!w-200px" clearable placeholder="请输入指标编码"
+                      @keyup.enter="handleQuery"/>
+          </el-form-item>
+          <el-form-item label="指标名称" prop="itemName">
+            <el-input v-model="queryParams.itemName" class="!w-200px" clearable placeholder="请输入指标名称"
+                      @keyup.enter="handleQuery"/>
+          </el-form-item>
+          <el-form-item label="指标类型" prop="itemType">
+            <el-select v-model="queryParams.itemType"  class="!w-200px" clearable placeholder="请选择指标类型">
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.IND_ITEM_TYPE)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="handleQuery">
+              <Icon class="mr-5px" icon="ep:search"/>
+              搜索
+            </el-button>
+            <el-button @click="resetQuery">
+              <Icon class="mr-5px" icon="ep:refresh"/>
+              重置
+            </el-button>
+            <el-button
+              v-hasPermi="['data:ind-item:create']"
+              plain
+              type="primary"
+              @click="openForm('create')"
+            >
+              <Icon class="mr-5px" icon="ep:plus"/>
+              新增
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </ContentWrap>
 
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list">
-      <el-table-column prop="itemNo" label="指标编码" header-align="center" align="center" min-width="80"/>
-      <el-table-column prop="itemName" label="指标名称" header-align="center" align="center" min-width="120"/>
-      <el-table-column prop="itemCategoryName" label="指标分类" header-align="center" align="center" min-width="100"/>
-      <el-table-column prop="itemType" label="指标类型" header-align="center" align="center" min-width="60">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.IND_ITEM_TYPE" :value="scope.row.itemType" />
-        </template>
-      </el-table-column>
-      <el-table-column prop="coefficient" label="系数" header-align="center" align="center" min-width="60"/>
-      <el-table-column prop="precision" label="指标精度" header-align="center" align="center" min-width="60"/>
-      <el-table-column prop="timeGranularity" label="时间粒度" header-align="center" align="center" min-width="40">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.TIME_GRANULARITY" :value="scope.row.timeGranularity" />
-        </template>
-      </el-table-column>
-      <el-table-column
-        :formatter="dateFormatter"
-        align="center"
-        label="创建时间"
-        prop="createTime"
-        width="180"/>
-      <el-table-column align="center" label="操作">
-        <template #default="scope">
-          <el-button
-            v-hasPermi="['data:ind-item:update']"
-            link
-            type="primary"
-            @click="openForm('update', scope.row)">
-            修改
-          </el-button>
-          <el-button
-            v-hasPermi="['data:ind-item:delete']"
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)">
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      v-model:limit="queryParams.pageSize"
-      v-model:page="queryParams.pageNo"
-      :total="total"
-      @pagination="getList"
-    />
-  </ContentWrap>
+      <!-- 列表 -->
+      <ContentWrap>
+        <el-table v-loading="loading" :data="list">
+          <el-table-column prop="itemNo" label="指标编码" header-align="center" align="center" min-width="70"/>
+          <el-table-column prop="itemName" label="指标名称" header-align="center" align="center" min-width="150"/>
+          <el-table-column prop="itemCategoryName" label="指标分类" header-align="center" align="center" min-width="80"/>
+          <el-table-column prop="itemType" label="指标类型" header-align="center" align="center" min-width="60">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.IND_ITEM_TYPE" :value="scope.row.itemType" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="coefficient" label="系数" header-align="center" align="center" min-width="50"/>
+          <el-table-column prop="precision" label="指标精度" header-align="center" align="center" min-width="50"/>
+          <el-table-column prop="timeGranularity" label="时间粒度" header-align="center" align="center" min-width="50">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.TIME_GRANULARITY" :value="scope.row.timeGranularity" />
+            </template>
+          </el-table-column>
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="创建时间"
+            prop="createTime"
+            width="200"/>
+          <el-table-column align="center" label="操作">
+            <template #default="scope">
+              <el-button
+                v-hasPermi="['data:ind-item:update']"
+                link
+                type="primary"
+                @click="openForm('update', scope.row)">
+                修改
+              </el-button>
+              <el-button
+                link
+                type="primary"
+                @click="getCurrentData(scope.row.itemNo)">
+                当前值
+              </el-button>
+              <el-button
+                link
+                type="primary"
+                @click="getHistoryData(scope.row)">
+                历史值
+              </el-button>
+              <el-button
+                v-hasPermi="['data:ind-item:delete']"
+                link
+                type="danger"
+                @click="handleDelete(scope.row.id)">
+                删除
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页 -->
+        <Pagination
+          v-model:limit="queryParams.pageSize"
+          v-model:page="queryParams.pageNo"
+          :total="total"
+          @pagination="getList"
+        />
+      </ContentWrap>
+    </el-col>
+  </el-row>
+
 
   <!-- 表单弹窗:添加/修改 -->
   <AtomIndDefineForm ref="atomFormRef" @success="getList" />
   <DerIndDefineForm ref="derFormRef" @success="getList" />
   <CalIndDefineForm ref="calFormRef" @success="getList" />
   <SelectItemType ref="itemTypeSel"/>
+  <IndCurrentData ref="indCurrentData"/>
+  <IndHistoryChart ref="indHistoryChart"/>
 </template>
 
 <script lang="ts" setup>
@@ -113,6 +144,10 @@
   import download from '@/utils/download'
   import * as ItemApi from '@/api/data/ind/item/item'
   import * as CategoryApi from "@/api/data/ind/category";
+  import IndCurrentData from './IndCurrentData.vue'
+  import IndHistoryChart from './IndHistoryChart.vue'
+  import {handleTree} from "@/utils/tree";
+
 
   defineOptions({ name: 'IndItem' })
 
@@ -140,10 +175,17 @@
       const data = await ItemApi.getItemPage(queryParams)
       list.value = data.list
       total.value = data.total
-      dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
     } finally {
       loading.value = false
     }
+  }
+
+  const getCategoryTree = async () => {
+    dataCategoryList.value = []
+    const res = await CategoryApi.getCategoryListAllSimple()
+    let category: Tree = {id: 0, label: '主类目', children: []}
+    category.children = handleTree(res, 'id', 'pid')
+    dataCategoryList.value.push(category)
   }
 
   /** 搜索按钮操作 */
@@ -182,7 +224,15 @@
     }else {
       itemTypeSel.value.open(type)
     }
+  }
+  const indCurrentData = ref()
+  const getCurrentData = (itemNo: string) => {
+    indCurrentData.value.open(itemNo)
+  }
 
+  const indHistoryChart = ref()
+  const getHistoryData = (raw: object) => {
+    indHistoryChart.value.open(raw)
   }
 
   /** 删除按钮操作 */
@@ -198,8 +248,18 @@
     } catch {}
   }
 
+  const handleNodeClick = (data: Tree) => {
+    if( data.id !== 0 ){
+      queryParams.itemCategory = String(data.id)
+    }else {
+      queryParams.itemCategory = ''
+    }
+    getList()
+  }
+
   /** 初始化 **/
   onMounted(() => {
     getList()
+    getCategoryTree()
   })
 </script>
diff --git a/src/views/data/plan/data/DataSetForm.vue b/src/views/data/plan/data/DataSetForm.vue
index 5c02606..7c4f382 100644
--- a/src/views/data/plan/data/DataSetForm.vue
+++ b/src/views/data/plan/data/DataSetForm.vue
@@ -22,7 +22,10 @@
       </el-form-item>
       <el-form-item label="查询语句" prop="querySql">
         <el-input v-model="formData.querySql" placeholder="请输入内容" type="textarea" maxlength="300"
-                  show-word-limit :rows="6" spellcheck="false"/>
+                  show-word-limit :rows="6" @input="checkSensitiveWords" spellcheck="false"/>
+      </el-form-item>
+      <el-form-item v-if="showError">
+        <p>输入中包含以下敏感词:<span style="color: red">{{sensitiveMessage}}</span></p>
       </el-form-item>
       <el-form-item label="备注" prop="remark">
         <el-input v-model="formData.remark" placeholder="请输入内容" type="textarea" maxlength="100"
@@ -30,15 +33,13 @@
       </el-form-item>
     </el-form>
     <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button :disabled="disableSubmit" type="primary" @click="submitForm">确 定</el-button>
       <el-button @click="dialogVisible = false">取 消</el-button>
     </template>
   </Dialog>
 </template>
 <script lang="ts" setup>
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import * as DataSetApi from '@/api/data/plan/data'
-import { CommonStatusEnum } from '@/utils/constants'
 import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
 
 defineOptions({ name: 'PlanDataSetForm' })
@@ -46,9 +47,15 @@
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
 
+const showError = ref(false)
+const foundSensitiveWords = ref()
+const sensitiveMessage = ref('')
+const sensitiveWords = [';', 'master', 'truncate', 'insert', 'delete', 'update', 'declare', 'alter', 'drop']
+
 const dialogVisible = ref(false) // 弹窗的是否展示
 const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formLoading = ref(false) // 表单的加载中:修改时的数据加载;
+const disableSubmit = ref(false) // 禁止提交
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const formData = ref({
   id: undefined,
@@ -84,10 +91,26 @@
       formLoading.value = false
     }
   }
-
-
 }
 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/**
+ * 验证敏感词
+ */
+const checkSensitiveWords = () => {
+  showError.value = false;
+  const regex = new RegExp(sensitiveWords.map(word => `${word}`).join('|'), 'gi');
+  let matches = formData.value.querySql.match(regex);
+  if (matches) {
+    showError.value = true;
+    foundSensitiveWords.value = Array.from(new Set(matches));
+    disableSubmit.value = true
+    sensitiveMessage.value = foundSensitiveWords.value.join('、')
+  } else {
+    foundSensitiveWords.value = undefined
+    disableSubmit.value = false
+  }
+}
 
 /** 提交表单 */
 const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
@@ -98,6 +121,7 @@
   if (!valid) return
   // 提交请求
   formLoading.value = true
+  disableSubmit.value = true
   try {
     const data = formData.value as DataSetApi.DataSetVO
     if (formType.value === 'create') {
@@ -112,6 +136,7 @@
     emit('success')
   } finally {
     formLoading.value = false
+    disableSubmit.value = false
   }
 }
 
diff --git a/src/views/data/point/DaPointChart.vue b/src/views/data/point/DaPointChart.vue
index caaa271..54e01c5 100644
--- a/src/views/data/point/DaPointChart.vue
+++ b/src/views/data/point/DaPointChart.vue
@@ -1,6 +1,6 @@
 <template>
   <el-dialog
-    title="采集值"
+    title="历史值"
     :close-on-click-modal="false"
     width="50%"
     v-model="visible"
diff --git a/src/views/data/point/DaPointForm.vue b/src/views/data/point/DaPointForm.vue
index 5a1682b..e4136b6 100644
--- a/src/views/data/point/DaPointForm.vue
+++ b/src/views/data/point/DaPointForm.vue
@@ -25,6 +25,7 @@
             <el-select
               v-model="formData.pointType"
               clearable
+              :disabled = "formType !== 'create'"
               placeholder="请选择测点类型"
             >
               <el-option
diff --git a/src/views/data/point/DaPointValue.vue b/src/views/data/point/DaPointValue.vue
new file mode 100644
index 0000000..671ede7
--- /dev/null
+++ b/src/views/data/point/DaPointValue.vue
@@ -0,0 +1,144 @@
+<template>
+  <el-dialog
+    title="当前值"
+    :close-on-click-modal="false"
+    width="50%"
+    v-model="visible"
+  >
+    <el-form
+      :model="dataForm"
+      v-loading="formLoading"
+      label-width="120px"
+    >
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="测点编码" prop="pointNo">
+            <el-input v-model="dataForm.pointNo" readonly/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="测点名称" prop="pointName">
+            <el-input v-model="dataForm.pointName" readonly/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="数据时间" prop="dataTime">
+            <el-input v-model="dataForm.dataTime" readonly/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="数据值" prop="dataValue">
+            <el-input v-model="dataForm.dataValue" readonly>
+              <template #append>{{ dataForm.unit }}</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <!-- 列表 -->
+    <ContentWrap v-if="dataForm.pointType === 'CALCULATE'">
+      <el-table border stripe v-loading="tableLoading" :data="list">
+        <el-table-column type="index" header-align="center" align="center" fixed="left" width="50"/>
+        <el-table-column fixed label="测点编码" header-align="center" align="left" min-width="150" prop="pointNo" />
+        <el-table-column fixed label="测点名称" header-align="center" align="left" min-width="240" prop="pointName" />
+        <el-table-column fixed label="当前值" header-align="center" align="left" min-width="150" prop="currentValue" />
+      </el-table>
+      <!-- 分页 -->
+      <Pagination
+        :total="total"
+        v-model:page="queryParams.pageNo"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </ContentWrap>
+
+  </el-dialog>
+</template>
+<script lang="ts" setup>
+
+import {reactive, ref} from "vue";
+import {getYMDHM0} from "@/utils/dateUtil";
+import * as DaPoint from "@/api/data/da/point/daPointChart";
+
+const message = useMessage() // 消息弹窗
+const visible = ref(false);
+const formLoading = ref(false)
+const tableLoading = ref(false)
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  pointNo: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+
+const dataForm = ref({
+  id: "",
+  pointNo: "",
+  pointName: "",
+  pointType: "",
+  unit: "",
+  dataTime: "",
+  dataValue: "",
+});
+
+/** 打开弹窗 */
+const open = async (row: object) => {
+  visible.value = true
+  resetForm()
+  dataForm.value.id = row.id;
+  dataForm.value.pointNo = row.pointNo;
+  dataForm.value.pointName = row.pointName;
+  dataForm.value.pointType = row.pointType;
+  dataForm.value.unit = row.unit;
+  getCurrentData()
+  queryParams.pointNo = row.pointNo;
+  if (dataForm.value.pointType === "CALCULATE") {
+    getList()
+  }
+}
+
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+async function getCurrentData() {
+  visible.value = true;
+  formLoading.value = true
+  if (dataForm.value.id) {
+    let params0 = [dataForm.value.pointNo]
+    const data = await DaPoint.getPointsRealValue(params0)
+    formLoading.value = false
+    dataForm.value.dataTime = getYMDHM0(new Date());
+    dataForm.value.dataValue = data[dataForm.value.pointNo]
+  }
+}
+
+/** 查询列表 */
+const getList = async () => {
+  tableLoading.value = true
+  try {
+    const page = await DaPoint.getMathPointCurrentValue(queryParams)
+    list.value = page.list
+    total.value = page.total
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  dataForm.value = {
+    id: undefined,
+    pointNo: undefined,
+    pointName: undefined,
+    pointType: undefined,
+    unit: undefined,
+    dataTime: undefined,
+    dataValue: undefined,
+  }
+}
+
+</script>
diff --git a/src/views/data/point/index.vue b/src/views/data/point/index.vue
index b11294c..1adf9d0 100644
--- a/src/views/data/point/index.vue
+++ b/src/views/data/point/index.vue
@@ -26,6 +26,21 @@
           class="!w-200px"
         />
       </el-form-item>
+      <el-form-item label="测点类型" prop="pointType">
+        <el-select
+          v-model="queryParams.pointType"
+          placeholder="请选择"
+          clearable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="dict in getStrDictOptions(DICT_TYPE.DATA_POINT_TYPE)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="测点Tag" prop="tagNo">
         <el-input
           v-model="queryParams.tagNo"
@@ -145,7 +160,7 @@
         </template>
       </el-table-column>
 
-      <el-table-column label="操作" align="center" min-width="130" fixed="right" width="120">
+      <el-table-column label="操作"  align="center" min-width="130" fixed="right" width="140">
         <template #default="scope">
           <el-button
             link
@@ -156,7 +171,7 @@
           >
             编辑
           </el-button>
-          <el-button link size="mini" type="primary" @click="chartHandle(scope.row)">数据</el-button>
+          <el-button link size="mini" type="primary" @click="chartHandle(scope.row)">历史值</el-button>
           <el-button
             link
             size="mini"
@@ -166,6 +181,7 @@
           >
             删除
           </el-button>
+          <el-button link size="mini" type="primary" @click="pointValueHandle(scope.row)">当前值</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -181,9 +197,13 @@
   <!-- 表单弹窗:添加/修改 -->
   <DaPointForm ref="formRef" @success="getList" />
 
+  <!-- 历史值 -->
   <DaPointChart ref="chartView" />
 
-  <!-- 用户导入对话框 -->
+  <!-- 当前值 -->
+  <DaPointValue ref="pointValue" />
+
+  <!-- 测点导入对话框 -->
   <PointImportForm ref="importFormRef" @success="getList" />
 </template>
 <script lang="ts" setup>
@@ -193,7 +213,7 @@
 import {DICT_TYPE, getDictOptions, getIntDictOptions, getStrDictOptions} from "@/utils/dict";
 import DaPointForm from './DaPointForm.vue'
 import DaPointChart from './DaPointChart.vue'
-import * as UserApi from "@/api/system/user";
+import DaPointValue from './DaPointValue.vue'
 import PointImportForm from './PointImportForm.vue'
 
 defineOptions({name: 'DataPoint'})
@@ -209,6 +229,7 @@
     pageSize: 10,
     pointNo: undefined,
     pointName: undefined,
+    pointType: undefined,
     tagNo: undefined,
     collectQuality: undefined,
   })
@@ -232,12 +253,18 @@
     getList()
   }
 
-  /** 查看数据操作 */
+  /** 查看历史值操作 */
   const chartView  = ref()
   const chartHandle = (raw: object) => {
     chartView.value.open(raw)
   }
 
+/** 查看当前值操作 */
+const pointValue  = ref()
+const pointValueHandle = (raw: object) => {
+  pointValue.value.open(raw)
+}
+
   /** 重置按钮操作 */
   const resetQuery = () => {
     queryFormRef.value.resetFields()
diff --git a/src/views/infra/apiAccessLog/index.vue b/src/views/infra/apiAccessLog/index.vue
index e3a6a7c..7e3a070 100644
--- a/src/views/infra/apiAccessLog/index.vue
+++ b/src/views/infra/apiAccessLog/index.vue
@@ -90,31 +90,31 @@
   <ContentWrap>
     <el-table v-loading="loading" :data="list">
       <el-table-column label="日志编号" align="center" prop="id" width="100" fix="right" />
-      <el-table-column label="用户编号" align="center" prop="userId" />
-      <el-table-column label="用户类型" align="center" prop="userType">
+      <el-table-column label="用户编号" align="center" prop="userId" width="80"/>
+      <el-table-column label="用户类型" align="center" prop="userType" width="100">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" />
         </template>
       </el-table-column>
-      <el-table-column label="应用名" align="center" prop="applicationName" width="150" />
+      <el-table-column label="应用名" align="center" prop="applicationName" width="120" />
       <el-table-column label="请求方法" align="center" prop="requestMethod" width="80" />
-      <el-table-column label="请求地址" align="center" prop="requestUrl" width="500" />
+      <el-table-column label="请求地址" align="center" prop="requestUrl" />
       <el-table-column label="请求时间" align="center" prop="beginTime" width="180">
         <template #default="scope">
           <span>{{ formatDate(scope.row.beginTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="执行时长" align="center" prop="duration" width="180">
+      <el-table-column label="执行时长" align="center" prop="duration" width="100">
         <template #default="scope"> {{ scope.row.duration }} ms </template>
       </el-table-column>
-      <el-table-column label="操作结果" align="center" prop="status">
+      <el-table-column label="操作结果" align="center" prop="status" width="120">
         <template #default="scope">
           {{ scope.row.resultCode === 0 ? '成功' : '失败(' + scope.row.resultMsg + ')' }}
         </template>
       </el-table-column>
       <el-table-column label="操作模块" align="center" prop="operateModule" width="180" />
       <el-table-column label="操作名" align="center" prop="operateName" width="180" />
-      <el-table-column label="操作类型" align="center" prop="operateType">
+      <el-table-column label="操作类型" align="center" prop="operateType" width="80">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.INFRA_OPERATE_TYPE" :value="scope.row.operateType" />
         </template>
diff --git a/src/views/infra/apiErrorLog/index.vue b/src/views/infra/apiErrorLog/index.vue
index 22f3116..c337684 100644
--- a/src/views/infra/apiErrorLog/index.vue
+++ b/src/views/infra/apiErrorLog/index.vue
@@ -86,16 +86,16 @@
   <!-- 列表 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list">
-      <el-table-column label="日志编号" align="center" prop="id" />
-      <el-table-column label="用户编号" align="center" prop="userId" />
-      <el-table-column label="用户类型" align="center" prop="userType">
+      <el-table-column label="日志编号" align="center" prop="id" width="100"/>
+      <el-table-column label="用户编号" align="center" prop="userId" width="80"/>
+      <el-table-column label="用户类型" align="center" prop="userType" width="100">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" />
         </template>
       </el-table-column>
-      <el-table-column label="应用名" align="center" prop="applicationName" width="200" />
+      <el-table-column label="应用名" align="center" prop="applicationName" width="120" />
       <el-table-column label="请求方法" align="center" prop="requestMethod" width="80" />
-      <el-table-column label="请求地址" align="center" prop="requestUrl" width="180" />
+      <el-table-column label="请求地址" align="center" prop="requestUrl" />
       <el-table-column
         label="异常发生时间"
         align="center"
@@ -103,8 +103,8 @@
         width="180"
         :formatter="dateFormatter"
       />
-      <el-table-column label="异常名" align="center" prop="exceptionName" width="180" />
-      <el-table-column label="处理状态" align="center" prop="processStatus">
+      <el-table-column label="异常名" align="center" prop="exceptionName" />
+      <el-table-column label="处理状态" align="center" prop="processStatus" width="100">
         <template #default="scope">
           <dict-tag
             :type="DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS"
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/model/matlab/model/MatlabModelForm.vue b/src/views/model/matlab/model/MatlabModelForm.vue
new file mode 100644
index 0000000..8cd37be
--- /dev/null
+++ b/src/views/model/matlab/model/MatlabModelForm.vue
@@ -0,0 +1,394 @@
+<template>
+  <div class="p-16px" style="background-color: #ffffff">
+    <el-header>
+      {{title}}
+    </el-header>
+    <el-main>
+      <el-form
+        ref="formRef"
+        v-loading="formLoading"
+        :model="formData"
+        :rules="formRules"
+        label-width="120px"
+      >
+        <el-divider content-position="left">模型信息</el-divider>
+        <el-row :gutter="8">
+          <el-col :span="12">
+            <el-form-item label="模型类型" prop="modelType">
+              <el-radio-group v-model="formData.modelType">
+                <el-radio-button :disabled="actionType == 'edit'"
+                  v-for="dict in getDictOptions(DICT_TYPE.MODEL_TYPE)"
+                  :key="dict.label"
+                  :label="dict.value"
+                >
+                  {{ dict.label }}
+                </el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="8">
+          <el-col :span="12">
+            <el-form-item label="模型名称" prop="modelName">
+              <el-input v-model="formData.modelName" placeholder=""/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="8">
+          <el-col :span="12">
+            <el-form-item label="模型文件" prop="modelFileName">
+              <el-input disabled v-model="formData.modelFileName" placeholder=""/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="4">
+            <el-upload
+              ref="uploadRef"
+              v-model:file-list="fileList"
+              :show-file-list="false"
+              :action="importUrl"
+              :auto-upload="true"
+              :disabled="uploadLoading"
+              v-loading="uploadLoading"
+              :before-upload="beforeUpload"
+              :headers="uploadHeaders"
+              :on-error="submitFormError"
+              :on-success="submitFormSuccess"
+              accept=".jar"
+            >
+              <el-tooltip content="上传算法封装.jar文件" placement="top" effect="light">
+                <el-button type="primary">
+                  <Icon icon="ep:upload"/>
+                  模型上传
+                </el-button>
+              </el-tooltip>
+            </el-upload>
+          </el-col>
+        </el-row>
+        <el-row :gutter="8">
+          <el-col :span="6">
+            <el-form-item label="MATLAB平台" prop="matlabPlatform">
+              <el-select v-model="formData.matlabPlatform">
+                <el-option
+                  v-for="item in getDictOptions(DICT_TYPE.MATLAB_PLATFORM)"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="MATLAB版本" prop="matlabVersion">
+              <el-select v-model="formData.matlabVersion">
+                <el-option
+                  v-for="item in getDictOptions(DICT_TYPE.MATLAB_VERSION)"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="formData.remark" placeholder="" type="textarea"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">模型方法</el-divider>
+<!--        <el-row :gutter="20">-->
+<!--          <el-col :span="4">-->
+<!--            <el-button type="primary" size="small" @click="addRow()" >新增</el-button>-->
+<!--          </el-col>-->
+<!--        </el-row>-->
+        <el-table :data="formData.modelMethods" border
+                  @expand-change="methodExpandChange" :expand-row-keys="methodExpandedRowKeys" :row-key="row => row.id">
+          <el-table-column
+            prop=""
+            label="全类名"
+            align="center"
+            width="400">
+            <template #default="scope">
+              <el-input disabled size="small" v-model="scope.row.className" placeholder=""/>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop=""
+            label="方法名"
+            align="center"
+            width="250">
+            <template #default="scope">
+              <el-input disabled size="small" v-model="scope.row.methodName" placeholder=""/>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop=""
+            label="数据长度"
+            align="center">
+            <template #default="scope">
+              <el-input-number disabled size="small" step-strictly v-model="scope.row.dataLength" :min="1"
+                               :max="50" value-on-clear="min"/>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop=""
+            label="输出长度"
+            align="center">
+            <template #default="scope">
+              <el-input-number disabled size="small" step-strictly v-model="scope.row.outLength" :min="1"
+                               :max="50" value-on-clear="min"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="方法参数" type="expand" width="100px">
+            <template #default="props">
+              <div class="m-16px">
+                <el-button type="primary" size="small" @click="addSetting(props.row.methodSettings)">新增参数</el-button>
+                <el-table :data="props.row.methodSettings" border size="small">
+                  <el-table-column align="center" label="参数名称" prop="name"/>
+                  <el-table-column align="center" label="key" prop="settingKey"/>
+                  <el-table-column align="center" label="value" prop="settingValue"/>
+                  <el-table-column align="center" label="参数类型" prop="valueType"/>
+                  <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100">
+                    <template #default="scope">
+                      <el-button
+                        @click="updateSetting(scope.row)"
+                        key="danger"
+                        type="primary"
+                        link
+                      >修改
+                      </el-button>
+                      <el-button
+                        @click="deleteSetting(props.row.methodSettings,scope.$index)"
+                        key="danger"
+                        type="danger"
+                        link
+                      >删除
+                      </el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100">
+            <template #default="scope">
+              <el-button
+                @click="deleteRow(scope.$index)"
+                key="danger"
+                type="danger"
+                link
+              >删除
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form>
+    </el-main>
+    <el-footer>
+      <div class="flex flex-row justify-end items-center">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+    </el-footer>
+  </div>
+  <SettingForm ref="settingFormRef"/>
+</template>
+<script lang="ts" setup>
+  import {DICT_TYPE,getDictOptions} from '@/utils/dict';
+  import * as MatlabApi from '@/api/model/matlab/mlModel'
+  import {FormRules} from 'element-plus'
+  import {getAccessToken, getTenantId} from "@/utils/auth";
+  import SettingForm from './MatlabModelSettingForm.vue'
+  import {generateUUID} from "@/utils";
+
+  const {t} = useI18n() // 国际化
+  const message = useMessage() // 消息弹窗
+  const title = ref('') // 弹窗的标题
+  const actionType = ref('') // 操作类型
+  const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+  const formType = ref('') // 表单的类型:create - 新增;update - 修改
+  const route = useRoute() // 路由
+  const router = useRouter();
+
+  const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
+
+/** settingForm弹窗 */
+  const settingFormRef = ref()
+  // 添加setting
+  const addSetting = (methodSettings) => {
+    settingFormRef.value.open(undefined,methodSettings)
+  }
+
+  // 修改setting
+  const updateSetting = (info) => {
+    settingFormRef.value.open(info)
+  }
+  // 删除setting
+  const deleteSetting = (methodSettings,index) => {
+    methodSettings.splice(index, 1);
+  }
+
+  const methodExpandedRowKeys = ref([])
+  const methodExpandChange = async (row: any, expandedRows: any[]) => {
+    methodExpandedRowKeys.value = expandedRows.map(e => e.id)
+  }
+
+  const formData = ref({
+    id: route.params.id,
+    modelName: undefined,
+    modelFileName: undefined,
+    modelFilePath: undefined,
+    modelType: 'predict',
+    matlabPlatform: undefined,
+    matlabVersion: undefined,
+    remark: undefined,
+    modelMethods: [],
+  })
+
+  const formRules = reactive<FormRules>({
+    modelFileName: [
+      {required: true, message: '模型名称不能为空,请上传模型jar文件', trigger: 'blur'}
+    ],
+    modelName: [
+      {required: true, message: '模型中文名称不能为空', trigger: 'blur'}
+    ],
+    modelType: [
+      {required: true, message: '模型类型不能为空', trigger: 'blur'}
+    ],
+    matlabPlatform: [
+      {required: true, message: 'MATLAB平台不能为空', trigger: 'blur'}
+    ],
+    matlabVersion: [
+      {required: true, message: 'MATLAB版本不能为空', trigger: 'blur'}
+    ],
+  })
+
+  const formRef = ref() // 表单 Ref
+
+  /** 提交表单 */
+  const submitForm = async () => {
+    // 校验表单
+    if (!formRef) return
+    const valid = await formRef.value.validate()
+    if (!valid) return
+    // 模型方法校验
+    if (formData.value.modelMethods?.length <= 0) {
+      message.error('模型方法为空')
+      return
+    }
+    // 模型方法名称校验
+    if (formData.value.modelMethods.some(e => e.methodName === undefined || e.methodName === '' || e.dataLength === undefined || e.dataLength === null)) {
+      message.error('存在不合法模型方法名')
+      return
+    }
+    // 提交请求
+    formLoading.value = true
+    try {
+      const data = formData.value
+      if (formType.value === 'create') {
+        await MatlabApi.create(data)
+        message.success(t('common.createSuccess'))
+      } else {
+        await MatlabApi.update(data)
+        message.success(t('common.updateSuccess'))
+      }
+    } finally {
+      formLoading.value = false
+    }
+    // router.push({path:'/model/mpk'})
+    router.back()
+  }
+
+  /** 重置表单 */
+  const resetForm = () => {
+    formData.value = {
+      id: undefined,
+      modelFileName: undefined,
+      modelName: undefined,
+      modelType: 'predict',
+      remark: undefined,
+      modelMethods: [],
+      modelFilePath: undefined
+    }
+    formRef.value?.resetFields()
+  }
+
+  const handleChange = function () {
+
+  }
+
+  const addRow = function () {
+    formData.value.modelMethods.push({
+      id: generateUUID(),
+      className: undefined,
+      methodName: undefined,
+      dataLength: 1,
+      outLength: 1,
+      methodSettings: []
+    })
+  }
+  const deleteRow = function (index) {
+    formData.value.modelMethods.splice(index, 1)
+  }
+
+  const fileList = ref([]) // 文件列表
+  const importUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/model/matlab/model/upload'
+  const uploadLoading = ref(false) // 表单的加载中
+  const uploadHeaders = ref() // 上传 Header 头
+  const beforeUpload = function (file) {
+    // 提交请求
+    uploadHeaders.value = {
+      Authorization: 'Bearer ' + getAccessToken(),
+      'tenant-id': getTenantId()
+    }
+    uploadLoading.value = true
+    return true;
+  }
+  const submitFormError = (): void => {
+    message.error('上传失败!')
+    uploadLoading.value = false
+  }
+  const submitFormSuccess = (response: any) => {
+    if (response.code !== 0) {
+      message.error(response.msg)
+      uploadLoading.value = false
+      return
+    }
+    const data = response.data;
+    formData.value.modelFilePath = data.filePath
+    formData.value.modelFileName = data.fileName
+    const modelMethods = (data.classInfos || []).flatMap(e => {
+      return (e.methodInfos || []).map(m => {
+        return { ...m,id: generateUUID(), className: e.className,methodSettings: [] };
+      });
+    });
+
+    formData.value.modelMethods = modelMethods
+    message.success('上传成功')
+    uploadLoading.value = false
+  }
+
+  onMounted(async () => {
+    const id = formData.value.id;
+    const type = id ? 'edit' : 'create'
+    actionType.value = type
+    title.value = t('action.' + type)
+    formType.value = type
+    resetForm()
+    // 修改时,设置数据
+    if (id) {
+      formLoading.value = true
+      try {
+        debugger
+        formData.value = await MatlabApi.get(id)
+        debugger
+      } finally {
+        formLoading.value = false
+      }
+    }
+  })
+</script>
+
+<style scoped lang="scss">
+</style>
diff --git a/src/views/model/matlab/model/MatlabModelSettingForm.vue b/src/views/model/matlab/model/MatlabModelSettingForm.vue
new file mode 100644
index 0000000..27c8931
--- /dev/null
+++ b/src/views/model/matlab/model/MatlabModelSettingForm.vue
@@ -0,0 +1,144 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+    >
+      <el-row :gutter="8">
+        <el-col :span="12">
+          <el-form-item label="参数名称" prop="name">
+            <el-input v-model="formData.name" placeholder=""/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="参数类型" prop="valueType">
+            <el-select v-model="formData.valueType">
+              <el-option
+                v-for="item in getDictOptions(DICT_TYPE.MODEL_METHOD_SETTING_VALUE_TYPE)"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="8">
+        <el-col :span="12">
+          <el-form-item label="key" prop="settingKey">
+            <el-input v-model="formData.settingKey" placeholder=""/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="value" prop="settingValue">
+            <el-input v-model="formData.settingValue" placeholder=""/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+  import {DICT_TYPE,getDictOptions} from '@/utils/dict';
+  import {FormRules} from 'element-plus'
+
+
+  const {t} = useI18n() // 国际化
+  const message = useMessage() // 消息弹窗
+
+  const dialogVisible = ref(false) // 弹窗的是否展示
+  const dialogTitle = ref('参数设置') // 弹窗的标题
+  const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+  const formData = ref({
+    settingKey: undefined,
+    settingValue: undefined,
+    name: undefined,
+    valueType: undefined
+  })
+
+
+  const formRules = reactive<FormRules>({
+    settingKey: [
+      {required: true, message: 'key不能为空', trigger: 'blur'},
+    ],
+    settingValue: [
+      {required: true, message: 'value不能为空', trigger: 'blur'},
+    ],
+    name: [
+      {required: true, message: '参数名称不能为空', trigger: 'blur'},
+    ],
+    valueType: [
+      {required: true, message: '参数类型不能为空', trigger: 'blur'},
+    ]
+  })
+
+  const formRef = ref() // 表单 Ref
+
+  let methodSettingsRef = undefined
+  let infoRef = undefined
+  /** 打开弹窗 */
+  const open = async (info,methodSettings) => {
+    dialogVisible.value = true
+    resetForm()
+    // 修改时,设置数据
+    if (info) {
+      infoRef = info
+      formLoading.value = true
+      try {
+        formData.value = {...info}
+      } finally {
+        formLoading.value = false
+      }
+    }else {
+      methodSettingsRef = methodSettings
+    }
+  }
+  defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+
+  // 数据回调
+  const emit = defineEmits(['addSettingCallback'])
+  /** 提交表单 */
+  const submitForm = async () => {
+    // 校验表单
+    if (!formRef) return
+    const valid = await formRef.value.validate()
+    if (!valid) return
+
+    // 提交请求
+    formLoading.value = true
+    try {
+      if (infoRef) {
+        // 修改
+        for (let key in formData.value) {
+          infoRef[key] = formData.value[key];
+        }
+        infoRef = undefined;
+      }else {
+        // 新增
+        methodSettingsRef.push({...formData.value})
+      }
+      dialogVisible.value = false
+    } finally {
+      formLoading.value = false
+    }
+  }
+
+  /** 重置表单 */
+  const resetForm = () => {
+    formData.value = {
+      settingKey: undefined,
+      settingValue: undefined,
+      name: undefined,
+      valueType: undefined,
+    }
+    formRef.value?.resetFields()
+  }
+</script>
diff --git a/src/views/model/matlab/model/MatlabRun.vue b/src/views/model/matlab/model/MatlabRun.vue
new file mode 100644
index 0000000..b43dc11
--- /dev/null
+++ b/src/views/model/matlab/model/MatlabRun.vue
@@ -0,0 +1,279 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle">
+    <el-form
+      class="-mb-15px"
+      :model="formData"
+      ref="formRef"
+      :inline="true"
+      :rules="formRules"
+      label-width="68px"
+      v-loading="formLoading"
+    >
+      <el-form-item style="width: 100%">
+        <el-divider content-position="left">模型信息</el-divider>
+      </el-form-item>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="全类名" style="width: 100%" prop="className">
+            <el-input disabled v-model="formData.className" placeholder=""/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="方法名" prop="methodName">
+            <el-select v-model="formData.methodId" @change="methodChange" style="width: 240px">
+              <el-option
+                v-for="item in methodList"
+                :key="item.id"
+                :label="item.className + '.' + item.methodName + '()'"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-divider content-position="left">模型参数信息</el-divider>
+      <div style="display:flex;flex-direction: row;align-items: center;margin-bottom: 6px">
+        <el-button tag="a" :href="staticDir + '/template/模型参数导入模板.xlsx'" download="模型参数导入模板.xlsx" style="text-decoration: none;" type="primary" size="small" link>模板下载</el-button>
+        <el-upload
+          ref="uploadRef"
+          v-model:file-list="fileList"
+          :show-file-list="false"
+          :action="importUrl"
+          :auto-upload="true"
+          :disabled="formLoading"
+          :before-upload="beforeUpload"
+          :headers="uploadHeaders"
+          :on-error="submitFormError"
+          :on-success="submitFormSuccess"
+          accept=".xlsx"
+        >
+          <el-button type="primary" size="small" link>参数导入</el-button>
+        </el-upload>
+      </div>
+      <el-row v-for="(item,index) in datas" :key="index" :gutter="20">
+        <el-col :span="24">
+          <el-form-item :label="'参数_' + (index)" required style="width: 100%">
+            <el-input type="textarea" :disabled="true" :rows="3" v-model="datas[index]" placeholder="" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-divider content-position="left">模型设置信息</el-divider>
+<!--      <el-row :gutter="20">-->
+<!--        <el-col :span="4">-->
+<!--          <el-button type="primary" size="small" @click="addRow()">新增</el-button>-->
+<!--        </el-col>-->
+<!--      </el-row>-->
+      <el-table :data="formData.modelSettings" border>
+        <el-table-column
+          prop=""
+          label="参数key"
+          align="center">
+          <template #default="scope">
+            <el-input size="small" v-model="scope.row.settingKey" :disabled="true" maxlength="50" clearable />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop=""
+          label="参数名称"
+          align="center">
+          <template #default="scope">
+            <el-input size="small" v-model="scope.row.name" :disabled="true" maxlength="50" clearable />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop=""
+          label="参数value"
+          align="center">
+          <template #default="scope">
+            <el-input size="small" v-model="scope.row.settingValue" :disabled="scope.row.settingKey === 'pyFile'" maxlength="50" clearable />
+          </template>
+        </el-table-column>
+<!--        <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100">-->
+<!--          <template #default="scope">-->
+<!--            <el-button-->
+<!--              @click="deleteRow(scope.$index)"-->
+<!--              key="danger"-->
+<!--              type="danger"-->
+<!--              :disabled="scope.row.settingKey === 'pyFile'"-->
+<!--              link-->
+<!--            >删除</el-button>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+      </el-table>
+      <el-divider content-position="left">模型运行结果</el-divider>
+      <el-input v-model="modelRunResult" placeholder="" rows="4" type="textarea" />
+      <div style="display: flex;flex-direction: row;justify-content: end;margin-top: 16px">
+        <el-button :loading="modelRunloading" type="primary" @click="modelRun()">运行</el-button>
+      </div>
+    </el-form>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+  import * as MlModelApi from '@/api/model/matlab/mlModel'
+  import {FormRules} from "element-plus";
+  import {getAccessToken, getTenantId} from "@/utils/auth";
+  import download from "@/utils/download";
+  const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
+
+  const { t } = useI18n() // 国际化
+  const message = useMessage() // 消息弹窗
+
+  const dialogVisible = ref(false) // 弹窗的是否展示
+  const dialogTitle = ref('模型运行') // 弹窗的标题
+
+  const formData = reactive({
+    modelFileName: '',
+    className: '',
+    methodName: '',
+    methodId: '',
+    uuids: [],
+    modelSettings: [],
+    outLength: 1
+  })
+
+  const datas = ref([])
+
+  // 模型方法下拉列表
+  const methodList = ref([])
+
+  /** 打开弹窗 */
+  const open = async (row) => {
+    dialogVisible.value = true
+    formData.modelFileName = row.modelFileName
+    const matlabModel = await MlModelApi.get(row.id)
+    methodList.value = matlabModel.modelMethods
+    formData.className = matlabModel.modelMethods[0].className
+    formData.methodId = matlabModel.modelMethods[0].id
+    formData.methodName = matlabModel.modelMethods[0].methodName
+    formData.outLength = matlabModel.modelMethods[0].outLength
+    datas.value = []
+    formData.uuids = [];
+    for (let i = 0 ; i < matlabModel.modelMethods[0].dataLength ; i++) {
+     datas.value[i] = '[[]]';
+     formData.uuids[i] = '';
+    }
+
+    // 回显参数
+    if (matlabModel.modelMethods[0].methodSettings && matlabModel.modelMethods[0].methodSettings.length > 0) {
+      formData.modelSettings = matlabModel.modelMethods[0].methodSettings
+    }
+
+  }
+  defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+  const formRules = reactive<FormRules>({
+    methodName: [
+      {required: true, message: '方法名不能为空', trigger: 'blur'}
+    ],
+    className: [
+      {required: true, message: '全类名不能为空', trigger: 'blur'}
+    ]
+  })
+
+  const addRow = function () {
+    formData.modelSettings.push({
+      settingKey: '',
+      settingValue: ''
+    })
+  }
+  const deleteRow = function (index) {
+    formData.modelSettings.splice(index, 1)
+  }
+  const methodChange = function (value) {
+    datas.value = []
+    formData.uuids = [];
+    var method = methodList.value.find(e => e.id === value);
+    formData.methodName = method.methodName
+    formData.className = method.className
+    for (let i = 0 ; i < method?.dataLength ; i++) {
+      datas.value[i] = '[[]]';
+      formData.uuids[i] = '';
+    }
+    // 回显参数
+    if (method.methodSettings && method.methodSettings.length > 0) {
+      formData.modelSettings = method.methodSettings
+    }else {
+      formData.modelSettings = []
+    }
+  }
+
+  const fileList = ref([]) // 文件列表
+  const importUrl =
+    import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/model/matlab/model/importData'
+  const formLoading = ref(false) // 表单的加载中
+  const uploadHeaders = ref() // 上传 Header 头
+  /** 上传错误提示 */
+  const submitFormError = (): void => {
+    message.error('导入失败,请检查导入文件!')
+    formLoading.value = false
+  }
+  const submitFormSuccess = (response: any) => {
+    try {
+      if (response.code !== 0) {
+        message.error(response.msg)
+        return
+      }
+      const data = response.data;
+      if (datas.value.length > data.length) {
+        message.error("导入数据长度为" + data.length + ",应≥" + datas.value.length)
+        return
+      }
+      for (let i = 0; i < datas.value.length; i++) {
+        datas.value[i] = data[i].data
+        formData.uuids[i] = data[i].uuid;
+      }
+      message.success('导入成功')
+    } finally {
+      formLoading.value = false
+    }
+  }
+  const beforeUpload = function (file) {
+    // 提交请求
+    uploadHeaders.value = {
+      Authorization: 'Bearer ' + getAccessToken(),
+      'tenant-id': getTenantId()
+    }
+    formLoading.value = true
+    return true;
+  }
+
+  // 模型运行结果
+  const modelRunResult = ref('')
+  // 模型运行loading
+  const modelRunloading = ref(false)
+  // 表单 Ref
+  const formRef = ref()
+  // 运行
+  const modelRun = async () => {
+    modelRunResult.value = ''
+  // 校验表单
+    if (!formRef) return
+    const valid = await formRef.value.validate()
+    if (!valid) return
+
+
+    // 提交请求
+    modelRunloading.value = true
+    try {
+      const data = {
+        ...formData
+      }
+
+      //处理modelSettings
+      // let settingsPredict = {};
+      // data.modelSettings.forEach(e => {
+      //   settingsPredict[e.settingKey] = e.settingValue;
+      // })
+      // data.modelSettings = settingsPredict
+
+      let result = await MlModelApi.test(data)
+
+      modelRunResult.value = result;
+      message.success('运行成功')
+    } finally {
+      modelRunloading.value = false
+    }
+  }
+</script>
diff --git a/src/views/model/matlab/model/index.vue b/src/views/model/matlab/model/index.vue
new file mode 100644
index 0000000..9f17b86
--- /dev/null
+++ b/src/views/model/matlab/model/index.vue
@@ -0,0 +1,188 @@
+<template>
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+      @submit.prevent
+    >
+      <el-form-item label="模型名称" prop="pyChineseName">
+        <el-input
+          v-model="queryParams.modelName"
+          placeholder="请输入模型名称"
+          clearable
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="模型文件" prop="modelFileName">
+        <el-input
+          v-model="queryParams.modelFileName"
+          placeholder="请输入模型文件名称"
+          clearable
+          class="!w-240px"
+        />
+      </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>
+        <div class="ml-12px">
+          <router-link :to="'/matlab/model/form'">
+            <el-button type="primary" plain v-hasPermi="['ml:model:create']">
+              <Icon icon="ep:plus" class="mr-5px"/>新增</el-button>
+          </router-link>
+        </div>
+
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+      row-key="id"
+    >
+      <el-table-column prop="modelName" label="模型名称" header-align="center" align="center" min-width="100" />
+      <el-table-column prop="modelFileName" label="模型文件" header-align="center" align="center" min-width="250"/>
+      <el-table-column prop="modelType" label="模型类型" header-align="center" align="center" :formatter="(r,c,v) => getDictLabel(DICT_TYPE.MODEL_TYPE,v)"/>
+      <el-table-column prop="matlabPlatform" label="matlab平台" header-align="center" align="center"/>
+      <el-table-column prop="matlabVersion" label="matlab版本" header-align="center" align="center"/>
+<!--      <el-table-column prop="remark" label="备注" header-align="center" align="center" min-width="100px"/>-->
+      <el-table-column prop="createDate" label="创建时间" header-align="center" align="center" :formatter="dateFormatter" width="180px"/>
+      <el-table-column prop="updateDate" label="修改时间" header-align="center" align="center" :formatter="dateFormatter" width="180px"/>
+      <el-table-column label="操作" align="center" width="200px">
+        <template #default="scope">
+          <div class="flex items-center justify-center">
+            <router-link :to="'/matlab/model/form/' + scope.row.id">
+              <el-button type="primary" link v-hasPermi="['ml:model:update']">
+                <Icon icon="ep:edit"/>修改
+              </el-button>
+            </router-link>
+            <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['ml:model:delete']">
+              <Icon icon="ep:delete"/>删除
+            </el-button>
+            <div class="pl-12px">
+              <el-dropdown @command="(command) => handleCommand(command, scope.row)" trigger="click">
+                <el-button type="primary" link>
+                  <Icon icon="ep:d-arrow-right" /> 更多
+                </el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      command="mpkRunDialog"
+                    >
+                      运行
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      v-model:limit="queryParams.limit"
+      v-model:page="queryParams.page"
+      :total="total"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <MatlabRun ref="matlabRun" />
+</template>
+<script lang="ts" setup>
+  import {dateFormatter} from '@/utils/formatTime'
+  import * as MlModelApi from '@/api/model/matlab/mlModel'
+  import { DICT_TYPE, getDictLabel } from '@/utils/dict'
+  import MatlabRun from './MatlabRun.vue'
+
+  defineOptions({name: 'MatlabModel'})
+
+  const message = useMessage() // 消息弹窗
+  const {t} = useI18n() // 国际化
+
+  const loading = ref(true) // 列表的加载中
+  const total = ref(0) // 列表的总页数
+  const list = ref([]) // 字典表格数据
+  const queryParams = reactive({
+    page: 1,
+    limit: 10,
+    modelName: '',
+    modelFileName: ''
+  })
+  const queryFormRef = ref() // 搜索的表单
+
+  const getList = async () => {
+    loading.value = true
+    try {
+      const data = await MlModelApi.getPage(queryParams)
+      list.value = data.list
+      total.value = data.total
+    } finally {
+      loading.value = false
+    }
+  }
+
+  /** 操作分发 */
+  const handleCommand = (command: string, row) => {
+    switch (command) {
+      case 'mpkRunDialog':
+        matlabRunDialog(row)
+        break
+      default:
+        break
+    }
+  }
+
+  /** 搜索按钮操作 */
+  const handleQuery = () => {
+    getList()
+  }
+
+  /** 重置按钮操作 */
+  const resetQuery = () => {
+    queryParams.page = 1
+    queryFormRef.value.resetFields()
+    handleQuery()
+  }
+
+  /** 删除按钮操作 */
+  const handleDelete = async (id: number) => {
+    try {
+      // 删除的二次确认
+      await message.delConfirm()
+      // 发起删除
+      await MlModelApi.deleteModel(id)
+      message.success(t('common.delSuccess'))
+      // 刷新列表
+      await getList()
+    } catch {
+    }
+  }
+
+  const matlabRun = ref();
+  const matlabRunDialog = (row) => {
+    matlabRun.value.open(row);
+  }
+
+  onActivated((to) => {
+    getList()
+  })
+
+  /** 初始化 **/
+  onMounted(async () => {
+    await getList()
+  })
+</script>
diff --git a/src/views/model/matlab/project/MatlabProjectForm.vue b/src/views/model/matlab/project/MatlabProjectForm.vue
new file mode 100644
index 0000000..e5b379d
--- /dev/null
+++ b/src/views/model/matlab/project/MatlabProjectForm.vue
@@ -0,0 +1,156 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+    >
+      <el-row :gutter="20">
+        <el-col :span="10">
+          <el-form-item label="项目名称" prop="projectName">
+            <el-input v-model="formData.projectName" placeholder=""/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="10">
+          <el-form-item label="项目编码" prop="projectCode">
+            <el-input v-model="formData.projectCode" placeholder=""/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="关联模型" prop="models">
+            <el-transfer style="width: 100%" :props="{key: 'id',label: 'modelFileName'}" :titles="['未选模型', '已选模型']" target-order="unshift" filterable :filter-method="filterMethod" v-model="formData.models" :data="modelList">
+              <template #default="{ option }">
+                <span :title="option.modelFileName + '【' + option.modelName + '】'">{{ option.modelFileName}}</span>
+              </template>
+            </el-transfer>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+  import * as ProjectApi from '@/api/model/matlab/project'
+  import * as MlModelApi from '@/api/model/matlab/mlModel'
+  import {FormRules} from 'element-plus'
+
+
+  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,
+    projectName: undefined,
+    projectCode: undefined,
+    models: undefined,
+  })
+
+
+  const formRules = reactive<FormRules>({
+    projectName: [
+      {required: true, message: '项目名称不能为空', trigger: 'blur'},
+    ],
+    projectCode: [
+      {required: true, message: '项目编码不能为空', trigger: 'blur'},
+    ],
+  })
+
+  const formRef = ref() // 表单 Ref
+
+  /** 打开弹窗 */
+  const open = async (type: string, id?: number) => {
+    dialogVisible.value = true
+    dialogTitle.value = t('action.' + type)
+    formType.value = type
+    getModelList();
+    resetForm()
+    // 修改时,设置数据
+    if (id) {
+      formLoading.value = true
+      try {
+        const data = await ProjectApi.getProject(id)
+        data.models = data.models.map(e => e.id)
+        formData.value = {
+          ...data
+        }
+      } finally {
+        formLoading.value = false
+      }
+    }
+  }
+  defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+  /** 提交表单 */
+  const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+  const submitForm = async () => {
+    // 校验表单
+    if (!formRef) return
+    const valid = await formRef.value.validate()
+    if (!valid) return
+    // 提交请求
+    formLoading.value = true
+    try {
+      const data = {
+        ...formData.value
+      }
+      if (data.models && data.models.length > 0) {
+        data.models = data.models.map(e => {
+          return {id: e}
+        })
+      }
+      if (formType.value === 'create') {
+        await ProjectApi.createProject(data)
+        message.success(t('common.createSuccess'))
+      } else {
+        await ProjectApi.updateProject(data)
+        message.success(t('common.updateSuccess'))
+      }
+      dialogVisible.value = false
+      // 发送操作成功的事件
+      emit('success')
+    } finally {
+      formLoading.value = false
+    }
+  }
+
+  /** 重置表单 */
+  const resetForm = () => {
+    formData.value = {
+      id: undefined,
+      projectName: undefined,
+      projectCode: undefined,
+    }
+    formRef.value?.resetFields()
+  }
+
+  // 所有模型列表
+  const modelList = ref([])
+  const getModelList = async () => {
+    modelList.value = await MlModelApi.list({})
+  }
+
+  // 模型筛选
+  const filterMethod = function (query, item) {
+    return item.modelFileName.toLowerCase().indexOf(query.toLowerCase()) !== -1
+  }
+</script>
+
+<style scoped>
+  :deep(.el-transfer-panel) {
+    width: 40%;
+  }
+</style>
diff --git a/src/views/model/matlab/project/MatlabProjectModelDialog.vue b/src/views/model/matlab/project/MatlabProjectModelDialog.vue
new file mode 100644
index 0000000..3d950aa
--- /dev/null
+++ b/src/views/model/matlab/project/MatlabProjectModelDialog.vue
@@ -0,0 +1,101 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="80%">
+    <!-- 搜索工作栏 -->
+    <ContentWrap>
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="68px"
+      >
+        <el-form-item label="模型名称" prop="modelFileName">
+          <el-input
+            v-model="queryParams.modelFileName"
+            placeholder="请输入模型名称"
+            clearable
+            class="!w-240px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="getList"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
+
+    <!-- 列表 -->
+    <ContentWrap>
+      <el-table
+        v-loading="loading"
+        :data="list"
+        row-key="id"
+        border
+      >
+        <el-table-column prop="modelName" label="模型中文名称"/>
+        <el-table-column prop="modelFileName" label="模型名称"/>
+        <el-table-column prop="modelType" label="模型类型" :formatter="(r,c,v) => getDictLabel(DICT_TYPE.MODEL_TYPE,v)"/>
+        <el-table-column prop="remark" label="备注" width="300px"/>
+        <el-table-column label="模型方法" type="expand" width="100px">
+          <template #default="props">
+            <el-table :data="props.row.modelMethods">
+              <el-table-column align="center" label="全类名" prop="className" />
+              <el-table-column align="center" label="方法名" prop="methodName" />
+              <el-table-column align="center" label="参数长度" prop="dataLength" />
+              <el-table-column align="center" label="输出长度" prop="outLength" />
+            </el-table>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <Pagination
+        v-model:limit="queryParams.pageSize"
+        v-model:page="queryParams.page"
+        :total="total"
+        @pagination="getList"
+      />
+    </ContentWrap>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+  import download from "@/utils/download";
+  import * as projectApi from '@/api/model/matlab/project'
+  import { dateFormatter } from '@/utils/formatTime'
+  import { DICT_TYPE, getDictLabel } from '@/utils/dict'
+
+
+  const { t } = useI18n() // 国际化
+  const message = useMessage() // 消息弹窗
+
+  const dialogVisible = ref(false) // 弹窗的是否展示
+  const dialogTitle = ref('关联模型') // 弹窗的标题
+
+  const loading = ref(true) // 列表的加载中
+  const total = ref(0) // 列表的总页数
+  const list = ref([]) // 字典表格数据
+  const queryParams = reactive({
+    page: 1,
+    pageSize: 10,
+    projectId: undefined,
+    modelFileName: undefined,
+  })
+
+  /** 打开弹窗 */
+  const open = async (projectId: String) => {
+    dialogVisible.value = true
+
+    queryParams.projectId = projectId;
+    getList()
+  }
+  defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+  const getList = async () => {
+    loading.value = true
+    try {
+      let data = await projectApi.getProjectModel(queryParams)
+      list.value = data.list
+      total.value = data.total
+    } finally {
+      loading.value = false
+    }
+  }
+</script>
diff --git a/src/views/model/matlab/project/index.vue b/src/views/model/matlab/project/index.vue
new file mode 100644
index 0000000..8f1799a
--- /dev/null
+++ b/src/views/model/matlab/project/index.vue
@@ -0,0 +1,221 @@
+<template>
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="项目名称" prop="projectName">
+        <el-input
+          v-model="queryParams.projectName"
+          placeholder="请输入项目名称"
+          clearable
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="项目编码" prop="projectCode">
+        <el-input
+          v-model="queryParams.projectCode"
+          placeholder="请输入项目编码"
+          clearable
+          class="!w-240px"
+        />
+      </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="['mpk:project:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px"/>
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+      row-key="id"
+    >
+      <el-table-column prop="projectName" label="项目名称"/>
+      <el-table-column prop="projectCode" label="项目编码"/>
+      <el-table-column prop="createTime" label="创建时间" :formatter="dateFormatter" width="300px"/>
+      <el-table-column label="操作" align="center" width="300px">
+        <template #default="scope">
+          <div class="flex items-center justify-center">
+            <el-button
+              link
+              type="primary"
+              @click="openForm('update', scope.row.id)"
+              v-hasPermi="['mpk:project:update']"
+            >
+              <Icon icon="ep:edit"/>
+              修改
+            </el-button>
+            <el-button
+              link
+              type="danger"
+              @click="handleDelete(scope.row.id)"
+              v-hasPermi="['mpk:project:delete']"
+            >
+              <Icon icon="ep:delete"/>
+              删除
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              @click="viewRelevanceModel(scope.row.id)"
+            >
+              <Icon icon="ep:link"/>
+              查看关联模型
+            </el-button>
+            <div class="pl-12px">
+              <el-dropdown @command="(command) => handleCommand(command, scope.row)"
+                           trigger="click">
+                <el-button type="primary" link>
+                  <Icon icon="ep:d-arrow-right"/>
+                  更多
+                </el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      command="publish"
+                    >
+                      <el-button link>发布</el-button>
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      v-model:limit="queryParams.limit"
+      v-model:page="queryParams.page"
+      :total="total"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <ProjectForm ref="formRef" @success="getList"/>
+  <!-- 关联模型 -->
+  <RelevanceModel ref="relevanceModelRef"/>
+</template>
+<script lang="ts" setup>
+  import {dateFormatter} from '@/utils/formatTime'
+  import * as ProjectApi from '@/api/model/matlab/project'
+  import ProjectForm from './MatlabProjectForm.vue'
+  import RelevanceModel from './MatlabProjectModelDialog.vue'
+
+  defineOptions({name: 'MatlabProject'})
+
+  const message = useMessage() // 消息弹窗
+  const {t} = useI18n() // 国际化
+
+  const loading = ref(true) // 列表的加载中
+  const total = ref(0) // 列表的总页数
+  const list = ref([]) // 字典表格数据
+  const queryParams = reactive({
+    page: 1,
+    limit: 10,
+    projectName: '',
+    projectCode: ''
+  })
+  const queryFormRef = ref() // 搜索的表单
+
+  const getList = async () => {
+    loading.value = true
+    try {
+      const data = await ProjectApi.getPage(queryParams)
+      list.value = data.list
+      total.value = data.total
+    } finally {
+      loading.value = false
+    }
+  }
+
+  /** 操作分发 */
+  const handleCommand = (command: string, row) => {
+    switch (command) {
+      case 'publish':
+        publish(row.id,row.projectName)
+        break
+      default:
+        break
+    }
+  }
+
+  // 发布
+  const publish = async (projectId,projectName) => {
+    // 发布的二次确认
+    await message.confirm('确认发布 ' + projectName)
+
+    // 发布
+    await ProjectApi.publish({projectId})
+
+    message.success('发布成功');
+  }
+
+  /** 搜索按钮操作 */
+  const handleQuery = () => {
+    getList()
+  }
+
+  /** 重置按钮操作 */
+  const resetQuery = () => {
+    queryParams.page = 1
+    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 ProjectApi.deleteProject(id)
+      message.success(t('common.delSuccess'))
+      // 刷新列表
+      await getList()
+    } catch {
+    }
+  }
+
+  // 查看关联模型
+  const relevanceModelRef = ref()
+  const viewRelevanceModel = (id) => {
+    relevanceModelRef.value.open(id)
+  }
+
+  /** 初始化 **/
+  onMounted(async () => {
+    await getList()
+  })
+</script>
diff --git a/src/views/model/mpk/file/MpkRun.vue b/src/views/model/mpk/file/MpkRun.vue
index 2b9039c..908d883 100644
--- a/src/views/model/mpk/file/MpkRun.vue
+++ b/src/views/model/mpk/file/MpkRun.vue
@@ -110,6 +110,7 @@
 <!--        </el-table-column>-->
       </el-table>
       <el-divider content-position="left">模型运行结果</el-divider>
+      <el-button type="primary" size="small" link @click="saveModel" v-if="showSaveModel && formData.methodName === 'train'">下载模型(.miail)</el-button>
       <el-input v-model="modelRunResult" placeholder="" rows="4" type="textarea" />
       <div style="display: flex;flex-direction: row;justify-content: end;margin-top: 16px">
         <el-button :loading="modelRunloading" type="primary" @click="modelRun()">运行</el-button>
@@ -121,6 +122,7 @@
   import * as MpkApi from '@/api/model/mpk/mpk'
   import {FormRules} from "element-plus";
   import {getAccessToken, getTenantId} from "@/utils/auth";
+  import download from "@/utils/download";
   const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
 
   const { t } = useI18n() // 国际化
@@ -167,6 +169,7 @@
         return e;
       })
     }
+
   }
   defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 
@@ -260,7 +263,8 @@
   // 运行
   const modelRun = async () => {
     modelRunResult.value = ''
-// 校验表单
+    showSaveModel.value = false
+  // 校验表单
     if (!formRef) return
     const valid = await formRef.value.validate()
     if (!valid) return
@@ -302,10 +306,63 @@
         data.model = undefined
       }
 
-      modelRunResult.value = await MpkApi.modelRun(data)
+      let result = await MpkApi.modelRun(data)
+
+      modelRunResult.value = result;
       message.success('运行成功')
+      // 训练方法
+      if (formData.methodName === 'train') {
+        result = JSON.parse(result);
+        // 返回结果正确
+        if (result?.status_code === '100' && result?.models?.model_path) {
+          // 有预测方法
+          if (methodList.value.some(e => e.methodName === 'predict')) {
+            saveModelParams.modelResult = result
+            saveModelParams.model = result?.models
+            showSaveModel.value = true
+          }
+        }
+      }
     } finally {
       modelRunloading.value = false
     }
   }
+
+  const showSaveModel = ref(false)
+
+  const saveModelParams = reactive({
+    pyName: '',
+    className: '',
+    methodName: '',
+    uuids: [],
+    modelSettings: [],
+    predModelSettings: [],
+    hasModel: false,
+    model: undefined,
+    modelResult: undefined,
+    dataLength: undefined,
+    resultKey: undefined,
+  })
+
+  const saveModel = async () => {
+    saveModelParams.className = formData.className
+    saveModelParams.pyName = formData.pyName
+    saveModelParams.modelSettings = formData.modelSettings
+    const predMethod = methodList.value.find(e => e.methodName === 'predict');
+    saveModelParams.methodName = predMethod.methodName
+    saveModelParams.resultKey = predMethod.resultKey
+    //predModelSettings
+    if (predMethod.methodSettings && predMethod.methodSettings.length > 0) {
+      saveModelParams.predModelSettings = predMethod.methodSettings.map(e => {
+        e.settingValue = e.value;
+        return e;
+      })
+    }
+    saveModelParams.hasModel = predMethod.model === 1
+
+    saveModelParams.dataLength = predMethod.dataLength
+
+    const data = await MpkApi.saveModel(saveModelParams)
+    download.downloadFile(data, saveModelParams.pyName + '.miail')
+  }
 </script>
diff --git a/src/views/model/mpk/file/index.vue b/src/views/model/mpk/file/index.vue
index a3d2bde..ff8f81e 100644
--- a/src/views/model/mpk/file/index.vue
+++ b/src/views/model/mpk/file/index.vue
@@ -1,7 +1,7 @@
 <template>
   <el-row :gutter="20">
     <!-- 左侧树 -->
-    <el-col :span="4" :xs="24">
+    <el-col :span="3" :xs="24">
       <ContentWrap class="h-1/1">
         <el-tree
           style="max-width: 600px"
@@ -13,7 +13,7 @@
         />
       </ContentWrap>
     </el-col>
-    <el-col :span="20" :xs="24">
+    <el-col :span="21" :xs="24">
       <!-- 搜索工作栏 -->
       <ContentWrap>
         <el-form
@@ -67,13 +67,14 @@
           :data="list"
           row-key="id"
         >
-          <el-table-column prop="pyChineseName" label="模型名称" header-align="center" align="left" min-width="100" />
-          <el-table-column prop="pyName" label="模型文件" header-align="center" align="left" min-width="300"/>
+          <el-table-column prop="pyChineseName" label="模型名称" header-align="center" align="center" min-width="100" />
+          <el-table-column prop="pyName" label="模型文件" header-align="center" align="center" min-width="200"/>
           <el-table-column prop="pyType" label="模型类型" :formatter="(r,c,v) => getDictLabel(DICT_TYPE.MODEL_TYPE,v)"/>
-          <el-table-column prop="menuName" label="所属菜单" min-width="120px"/>
-          <el-table-column prop="groupName" label="所属组" min-width="120px"/>
-          <el-table-column prop="remark" label="备注" min-width="100px"/>
-          <el-table-column prop="createDate" label="创建时间" :formatter="dateFormatter" width="180px"/>
+<!--          <el-table-column prop="menuName" label="所属菜单" min-width="120px"/>-->
+<!--          <el-table-column prop="groupName" label="所属组" min-width="120px"/>-->
+<!--          <el-table-column prop="remark" label="备注" min-width="100px"/>-->
+          <el-table-column prop="createDate" label="创建时间" align="center" :formatter="dateFormatter" width="180px"/>
+          <el-table-column prop="updateDate" label="修改时间" align="center" :formatter="dateFormatter" width="180px"/>
           <el-table-column label="操作" align="center" width="200px">
             <template #default="scope">
               <div class="flex items-center justify-center">
diff --git a/src/views/model/pre/accuracy/MmItemAccuracyHisForm.vue b/src/views/model/pre/accuracy/MmItemAccuracyHisForm.vue
new file mode 100644
index 0000000..3e5ac75
--- /dev/null
+++ b/src/views/model/pre/accuracy/MmItemAccuracyHisForm.vue
@@ -0,0 +1,122 @@
+<template>
+  <el-drawer
+    v-model="drawer"
+    size="60%"
+    title="历史详情"
+    direction="rtl"
+    :before-close="handleClose"
+  >
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="精准误差" prop="inDeviation">
+        <el-input
+          v-model="queryParams.inDeviation"
+          placeholder="请输入精准误差"
+          clearable
+          class="!w-240px"
+        />
+      </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-form-item>
+    </el-form>
+    <!-- 列表 -->
+    <el-table
+      v-loading="loading"
+      :data="list"
+      row-key="id"
+    >
+      <el-table-column prop="inDeviation" label="精准误差"/>
+      <el-table-column prop="inAccuracyRate" label="精准度"/>
+      <el-table-column prop="outDeviation" label="不可信误差"/>
+      <el-table-column prop="outAccuracyRate" label="不可信率"/>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      v-model:limit="queryParams.limit"
+      v-model:page="queryParams.page"
+      :total="total"
+      @pagination="getList"
+    />
+  </el-drawer>
+</template>
+<script lang="ts" setup>
+import * as MmItemAccuracyHis from '@/api/model/pre/accuracy/his'
+
+defineOptions({name: 'ChartParam'})
+
+const drawer = ref(false)
+const loading = ref(false) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 字典表格数据
+const queryParams = reactive({
+  page: 1,
+  limit: 10,
+  rateId: undefined,
+  inDeviation: undefined,
+  inAccuracyRate: undefined,
+  outDeviation: undefined,
+  outAccuracyRate: undefined,
+})
+const queryFormRef = ref() // 搜索的表单
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await MmItemAccuracyHis.getMmItemAccuracyHisPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 打开弹窗 */
+const open = async (rateId?: string) => {
+  resetForm()
+  drawer.value = true
+  queryParams.rateId = rateId
+  if (rateId) {
+    getList()
+  }
+}
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+/** 重置表单 */
+const resetForm = () => {
+  queryParams.rateId = undefined
+  queryParams.inDeviation = undefined
+  queryParams.inAccuracyRate = undefined
+  queryParams.outDeviation = undefined
+  queryParams.outAccuracyRate = undefined
+
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+const handleClose = (done: () => void) => {
+  drawer.value = false
+}
+</script>
diff --git a/src/views/model/pre/accuracy/MmItemAccuracyRateForm.vue b/src/views/model/pre/accuracy/MmItemAccuracyRateForm.vue
new file mode 100644
index 0000000..10e2572
--- /dev/null
+++ b/src/views/model/pre/accuracy/MmItemAccuracyRateForm.vue
@@ -0,0 +1,176 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="120px"
+    >
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预测项ID" prop="itemId">
+            <el-input v-model="formData.itemId" placeholder="请输入预测项ID"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="输出ID" prop="outId">
+            <el-input v-model="formData.outId" placeholder="请输入输出ID"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="取样长度" prop="sampleLength">
+            <el-input-number v-model="formData.sampleLength" :min="1" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="值类型" prop="valueType">
+            <el-input v-model="formData.valueType" placeholder="请输入值类型"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="开始统计时间" prop="beginTime">
+            <el-input v-model="formData.beginTime" placeholder="请输入开始统计时间"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="精准误差" prop="inDeviation">
+            <el-input v-model="formData.inDeviation" placeholder="请输入精准误差"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="精准度" prop="inAccuracyRate">
+            <el-input v-model="formData.inAccuracyRate" placeholder="请输入精准度"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="不可信误差" prop="outDeviation">
+            <el-input v-model="formData.outDeviation" placeholder="请输入不可信误差"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="不可信率" prop="outAccuracyRate">
+            <el-input v-model="formData.outAccuracyRate" placeholder="请输入不可信率"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="是否启用" prop="isEnable">
+            <el-select v-model="formData.isEnable" placeholder="请选择">
+              <el-option
+                v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as MmItemAccuracyRate from '@/api/model/pre/accuracy/rate'
+import {DICT_TYPE, getIntDictOptions} from "@/utils/dict";
+
+defineOptions({name: 'DataMmItemAccuracyRateForm'})
+
+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,
+  itemId: undefined,
+  outId: undefined,
+  sampleLength: undefined,
+  valueType: undefined,
+  beginTime: undefined,
+  inDeviation: undefined,
+  inAccuracyRate: undefined,
+  outDeviation: undefined,
+  outAccuracyRate: undefined,
+  isEnable: undefined,
+})
+const formRules = reactive({
+  itemId: [{required: true, message: '预测项ID不能为空', trigger: 'blur'}],
+  outId: [{required: true, message: '输出ID不能为空', 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 MmItemAccuracyRate.getMmItemAccuracyRate(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as MmItemAccuracyRate.MmItemAccuracyRateVO
+    if (formType.value === 'create') {
+      await MmItemAccuracyRate.createMmItemAccuracyRate(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await MmItemAccuracyRate.updateMmItemAccuracyRate(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    itemId: undefined,
+    outId: undefined,
+    sampleLength: undefined,
+    valueType: undefined,
+    beginTime: undefined,
+    inDeviation: undefined,
+    inAccuracyRate: undefined,
+    outDeviation: undefined,
+    outAccuracyRate: undefined,
+    isEnable: undefined,
+  }
+  formRef.value?.resetFields()
+}
+</script>
diff --git a/src/views/model/pre/accuracy/index.vue b/src/views/model/pre/accuracy/index.vue
new file mode 100644
index 0000000..04bccd9
--- /dev/null
+++ b/src/views/model/pre/accuracy/index.vue
@@ -0,0 +1,146 @@
+<template>
+  <!-- 搜索 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['item:accuracy-rate:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" />
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="预测项ID" header-align="center" align="center" min-width="150" prop="itemId" fixed/>
+      <el-table-column label="输出ID" header-align="center" align="center" min-width="150" prop="outId" />
+      <el-table-column label="取样长度" header-align="center" align="center" min-width="100" prop="sampleLength" />
+      <el-table-column label="值类型" header-align="center" align="center" min-width="50" prop="valueType" />
+      <el-table-column label="开始统计时间" header-align="center" align="center" min-width="50" prop="beginTime" />
+      <el-table-column label="精准误差" header-align="center" align="center" min-width="50" prop="inDeviation" />
+      <el-table-column label="精准度" header-align="center" align="center" min-width="100" prop="inAccuracyRate" />
+      <el-table-column label="不可信误差" header-align="center" align="center" min-width="100" prop="outDeviation" />
+      <el-table-column label="不可信率" header-align="center" align="center" min-width="100" prop="outAccuracyRate" />
+      <el-table-column label="是否启用" header-align="center" align="center" min-width="100" prop="isEnable" >
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COM_IS_INT" :value="scope.row.isEnable" />
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" header-align="center" align="center" min-width="100" prop="createTime" />
+      <el-table-column label="操作" align="center" min-width="180" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['item:accuracy-rate:update']"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['item:accuracy-rate:delete']"
+          >
+            删除
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleView(scope.row.id)"
+          >
+            查看历史
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <MmItemAccuracyRateForm ref="formRef" @success="getList" />
+  <MmItemAccuracyHis ref="MessageRef" />
+
+</template>
+<script lang="ts" setup>
+import MmItemAccuracyRateForm from './MmItemAccuracyRateForm.vue'
+import MmItemAccuracyHis from './MmItemAccuracyHisForm.vue'
+import * as MmItemAccuracyRate from '@/api/model/pre/accuracy/rate'
+import {DICT_TYPE} from "@/utils/dict";
+
+defineOptions({name: 'DataMmItemAccuracyRate'})
+
+const message = useMessage() // 消息弹窗
+const {t} = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const page = await MmItemAccuracyRate.getMmItemAccuracyRatePage(queryParams)
+    list.value = page.list
+    total.value = page.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await MmItemAccuracyRate.deleteMmItemAccuracyRate(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {
+  }
+}
+
+/** 查看历史按钮操作 */
+const MessageRef = ref()
+const handleView = (id?: string) => {
+  MessageRef.value.open(id)
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  await getList()
+})
+</script>
diff --git a/src/views/model/pre/alarm/MmPredictAlarmConfigForm.vue b/src/views/model/pre/alarm/MmPredictAlarmConfigForm.vue
new file mode 100644
index 0000000..c266a1f
--- /dev/null
+++ b/src/views/model/pre/alarm/MmPredictAlarmConfigForm.vue
@@ -0,0 +1,239 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="120px"
+    >
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="消息标题" prop="title">
+            <el-input v-model="formData.title" placeholder="请输入消息标题"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="监控对象" prop="alarmObj">
+            <el-input v-model="formData.alarmObj" placeholder="请输入监控对象"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预测项" prop="itemId">
+            <el-select
+              v-model="formData.itemId"
+              filterable
+              @change="handleChange(formData.itemId)"
+              placeholder="请选择">
+              <el-option
+                v-for="(item, index) in predictItemList"
+                :key="index"
+                :label="item.itemname"
+                :value="item.id"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="输出" prop="outId">
+            <el-select
+              v-model="formData.outId"
+              filterable
+              placeholder="请选择">
+              <el-option
+                v-for="(item, index) in outPutList"
+                :key="index"
+                :label="item.tagname"
+                :value="item.id"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="比较长度" prop="compLength">
+            <el-input-number v-model="formData.compLength" :min="1" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="单位" prop="unit">
+            <el-input v-model="formData.unit" placeholder="请输入单位"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预测上限" prop="upperLimit">
+            <el-input-number v-model="formData.upperLimit" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="预测下限" prop="lowerLimit">
+            <el-input-number v-model="formData.lowerLimit" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="累计上限" prop="culUpper">
+            <el-input-number v-model="formData.culUpper" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="累计下限" prop="culLower">
+            <el-input-number v-model="formData.culLower" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="转换系数" prop="coefficient">
+            <el-input-number v-model="formData.coefficient" :min="1" clearable controls-position="right" style="width: 100%"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="调度方案" prop="scheduleId">
+            <el-input v-model="formData.scheduleId" placeholder="请输入调度方案"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="是否启用" prop="isEnable">
+            <el-select v-model="formData.isEnable" placeholder="请选择">
+              <el-option
+                v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as MmPredictAlarmConfig from '@/api/model/pre/alarm/config'
+import {DICT_TYPE, getIntDictOptions} from "@/utils/dict";
+import * as MmPredictItem from '@/api/model/pre/item'
+
+defineOptions({name: 'DataMmPredictAlarmConfigForm'})
+
+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 predictItemList = ref([])
+const outPutList = ref([])
+const formData = ref({
+  id: undefined,
+  title: undefined,
+  alarmObj: undefined,
+  itemId: undefined,
+  itemName: undefined,
+  outId: undefined,
+  compLength: undefined,
+  upperLimit: undefined,
+  lowerLimit: undefined,
+  culUpper: undefined,
+  culLower: undefined,
+  unit: undefined,
+  coefficient: undefined,
+  scheduleId: undefined,
+  isEnable: undefined,
+})
+const formRules = reactive({
+  title: [{required: true, message: '消息标题不能为空', trigger: 'blur'}],
+  alarmObj: [{required: true, message: '监控对象不能为空', trigger: 'blur'}],
+  itemId: [{required: true, message: '预测项ID不能为空', trigger: 'blur'}],
+  outId: [{required: true, message: '输出ID不能为空', 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()
+
+  // 获取normal列表
+  predictItemList.value = await MmPredictItem.getMmPredictItemList({
+    status: 1
+  })
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await MmPredictAlarmConfig.getMmPredictAlarmConfig(id)
+      await handleChange(formData.value.itemId)
+    } finally {
+      formLoading.value = false
+    }
+  }
+
+}
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as MmPredictAlarmConfig.MmPredictAlarmConfigVO
+    if (formType.value === 'create') {
+      await MmPredictAlarmConfig.createMmPredictAlarmConfig(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await MmPredictAlarmConfig.updateMmPredictAlarmConfig(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+async function handleChange(itemid) {
+  const queryParams = reactive({
+    itemid: itemid,
+  })
+  outPutList.value = await MmPredictItem.getMmItemOutputList(queryParams)
+}
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    title: undefined,
+    alarmObj: undefined,
+    itemId: undefined,
+    outId: undefined,
+    compLength: 1,
+    upperLimit: undefined,
+    lowerLimit: undefined,
+    culUpper: undefined,
+    culLower: undefined,
+    unit: undefined,
+    coefficient: 1,
+    scheduleId: undefined,
+    isEnable: undefined,
+  }
+  formRef.value?.resetFields()
+}
+</script>
diff --git a/src/views/model/pre/alarm/MmPredictAlarmMessageForm.vue b/src/views/model/pre/alarm/MmPredictAlarmMessageForm.vue
new file mode 100644
index 0000000..c84a693
--- /dev/null
+++ b/src/views/model/pre/alarm/MmPredictAlarmMessageForm.vue
@@ -0,0 +1,135 @@
+<template>
+  <el-drawer
+    v-model="drawer"
+    size="60%"
+    title="历史详情"
+    direction="rtl"
+    :before-close="handleClose"
+  >
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="消息标题" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入消息标题"
+          clearable
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="监控对象" prop="alarmObj">
+        <el-input
+          v-model="queryParams.alarmObj"
+          placeholder="请输入监控对象"
+          clearable
+          class="!w-240px"
+        />
+      </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-form-item>
+    </el-form>
+    <!-- 列表 -->
+    <el-table
+      v-loading="loading"
+      :data="list"
+      row-key="id"
+    >
+      <el-table-column prop="title" label="消息标题" header-align="center" align="left" min-width="150"/>
+      <el-table-column prop="content" label="消息内容" header-align="center" align="left" min-width="200"/>
+      <el-table-column prop="alarmObj" label="监控对象" align="left" min-width="150"/>
+      <el-table-column prop="alarmType" label="预警类型" align="left" min-width="150"/>
+      <el-table-column prop="alarmTime" label="预警时间" align="left" min-width="150"/>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      v-model:limit="queryParams.pageSize"
+      v-model:page="queryParams.pageNo"
+      :total="total"
+      @pagination="getList"
+    />
+  </el-drawer>
+</template>
+<script lang="ts" setup>
+import * as MmPredictAlarmMessage from '@/api/model/pre/alarm/message'
+
+defineOptions({name: 'ChartParam'})
+
+const drawer = ref(false)
+const loading = ref(false) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 字典表格数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  configId: undefined,
+  title: undefined,
+  content: undefined,
+  alarmObj: undefined,
+  pointId: undefined,
+  itemId: undefined,
+  outId: undefined,
+  currentValue: undefined,
+  outTime: undefined,
+  outValue: undefined,
+  alarmType: undefined,
+  alarmTime: undefined,
+  createTime: undefined,
+})
+const queryFormRef = ref() // 搜索的表单
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await MmPredictAlarmMessage.getMmPredictAlarmMessagePage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 打开弹窗 */
+const open = async (configId?: string) => {
+  resetForm()
+  drawer.value = true
+  queryParams.configId = configId
+  if (configId) {
+    getList()
+  }
+}
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+/** 重置表单 */
+const resetForm = () => {
+  queryParams.title = undefined
+  queryParams.pointId = undefined
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+const handleClose = (done: () => void) => {
+  drawer.value = false
+}
+</script>
diff --git a/src/views/model/pre/alarm/index.vue b/src/views/model/pre/alarm/index.vue
new file mode 100644
index 0000000..651f6cc
--- /dev/null
+++ b/src/views/model/pre/alarm/index.vue
@@ -0,0 +1,178 @@
+<template>
+  <!-- 搜索 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="名称" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入名称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </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="['pre:alarm-config:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" />
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="消息标题" header-align="center" align="left" min-width="150" prop="title" fixed/>
+      <el-table-column label="监控对象" header-align="center" align="center" min-width="150" prop="alarmObj" />
+      <el-table-column label="预测项" header-align="center" align="left" min-width="150" prop="itemName" />
+      <el-table-column label="输出" header-align="center" align="center" min-width="150" prop="outName" />
+      <el-table-column label="比较长度" header-align="center" align="center" min-width="100" prop="compLength" />
+      <el-table-column label="预测上限" header-align="center" align="center" min-width="100" prop="upperLimit" />
+      <el-table-column label="预测下限" header-align="center" align="center" min-width="100" prop="lowerLimit" />
+      <el-table-column label="累计上限" header-align="center" align="center" min-width="100" prop="culUpper" />
+      <el-table-column label="累计下限" header-align="center" align="center" min-width="100" prop="culLower" />
+      <el-table-column label="单位" header-align="center" align="center" min-width="100" prop="unit" />
+      <el-table-column label="转换系数" header-align="center" align="center" min-width="100" prop="coefficient" />
+      <el-table-column label="是否启用" header-align="center" align="center" min-width="100" prop="isEnable" >
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COM_IS_INT" :value="scope.row.isEnable" />
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" min-width="160" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['pre:alarm-config:update']"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['pre:alarm-config:delete']"
+          >
+            删除
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            @click="handleView(scope.row.id)"
+          >
+            记录
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <MmPredictAlarmConfigForm ref="formRef" @success="getList" />
+  <MmPredictAlarmMessage ref="MessageRef" />
+
+</template>
+<script lang="ts" setup>
+import MmPredictAlarmConfigForm from './MmPredictAlarmConfigForm.vue'
+import MmPredictAlarmMessage from './MmPredictAlarmMessageForm.vue'
+import * as MmPredictAlarmConfig from '@/api/model/pre/alarm/config'
+import {DICT_TYPE} from "@/utils/dict";
+
+defineOptions({name: 'DataMmPredictAlarmConfig'})
+
+  const message = useMessage() // 消息弹窗
+  const {t} = useI18n() // 国际化
+
+  const loading = ref(true) // 列表的加载中
+  const total = ref(0) // 列表的总页数
+  const list = ref([]) // 列表的数据
+  const queryParams = reactive({
+    pageNo: 1,
+    pageSize: 10,
+    title: undefined,
+  })
+  const queryFormRef = ref() // 搜索的表单
+
+  /** 查询列表 */
+  const getList = async () => {
+    loading.value = true
+    try {
+      const page = await MmPredictAlarmConfig.getMmPredictAlarmConfigPage(queryParams)
+      list.value = page.list
+      total.value = page.total
+    } finally {
+      loading.value = false
+    }
+  }
+
+  /** 搜索按钮操作 */
+  const handleQuery = () => {
+    queryParams.pageNo = 1
+    getList()
+  }
+
+  /** 重置按钮操作 */
+  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 MmPredictAlarmConfig.deleteMmPredictAlarmConfig(id)
+      message.success(t('common.delSuccess'))
+      // 刷新列表
+      await getList()
+    } catch {
+    }
+  }
+
+  /** 查看历史按钮操作 */
+  const MessageRef = ref()
+  const handleView = (id?: string) => {
+    MessageRef.value.open(id)
+  }
+
+  /** 初始化 **/
+  onMounted(async () => {
+    await getList()
+  })
+</script>
diff --git a/src/views/model/pre/analysis/index.vue b/src/views/model/pre/analysis/index.vue
index ef66aba..dea20fd 100644
--- a/src/views/model/pre/analysis/index.vue
+++ b/src/views/model/pre/analysis/index.vue
@@ -1,7 +1,7 @@
 <template>
   <el-card shadow="never" class="aui-card--fill">
     <div class="mod-his__index">
-      <el-form :inline="true" :model="formData" label-width="80px">
+      <el-form :inline="true" :model="formData" label-width="70px">
         <el-form-item label="开始时间">
           <el-date-picker
             v-model="formData.startTime"
@@ -33,13 +33,25 @@
         </el-form-item>
         <el-form-item>
           <el-button-group>
-            <el-button type="primary" plain :icon="ArrowLeft"
+            <el-button type="primary" plain :icon="DArrowLeft"
                        :loading="loading1" @click="leftSearchDataByRange()"/>
             <el-button type="primary" plain :icon="Search"
                        :loading="loading1" @click="getList()">查询
             </el-button>
-            <el-button type="primary" plain :icon="ArrowRight"
+            <el-button type="primary" plain :icon="DArrowRight"
                        :loading="loading1" @click="rightSearchDataByRange()"/>
+          </el-button-group>
+        </el-form-item>
+        <el-form-item>
+          <el-button-group>
+            <el-button type="primary" plain :icon="CaretLeft"
+                       @click="playChart(true)"/>
+            <el-button type="primary" plain :icon="VideoPlay" v-if="!isPlay"
+                       @click="playHandle('play')"/>
+            <el-button type="primary" plain :icon="VideoPause" v-if="isPlay"
+                       @click="playHandle('pause')"/>
+            <el-button type="primary" plain :icon="CaretRight"
+                       @click="playChart(false)"/>
           </el-button-group>
         </el-form-item>
 
@@ -154,7 +166,7 @@
               </el-form>
               <el-form :inline="true" :model="formData" label-width="100px">
                 <el-row>
-                  <el-col :span="12">
+                  <el-col :span="16">
                     <el-form-item label="数据类型">
                       <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck">
                         <el-checkbox v-for="item in formData.chartOptions" :label="item"
@@ -187,7 +199,7 @@
 import {getYMDHMS} from "@/utils/dateUtil"
 import * as McsApi from '@/api/model/mcs'
 import * as echarts from "echarts";
-import {Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue'
+import {Search, DArrowLeft, DArrowRight, VideoPlay, VideoPause, CaretLeft, CaretRight} from '@element-plus/icons-vue'
 
 defineOptions({name: 'AnalysisformData'})
 
@@ -213,7 +225,7 @@
   currentStamp60: '',
   predictStamp: '',
   chartCheck: ['T+L', '真实值'],
-  chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'],
+  chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值', '预测累计', '真实累计'],
   checkedItemData: [],
   backItem: '',
   backValue: 0,
@@ -223,13 +235,13 @@
   queryStep: 2,
   isMultipleYRadio: '单坐标轴',
   isMultipleY: false,
-  predictFreq: 3,
+  predictFreq: 2,
 })
 const calRateFormRef = ref()
 const calRateForm = ref({
   calItem: undefined,
-  IN_DEVIATION: 0,
-  OUT_DEVIATION: 0,
+  IN_DEVIATION: 10,
+  OUT_DEVIATION: 50,
   IN_ACCURACY_RATE: 0,
   OUT_ACCURACY_RATE: 0,
   itemAvg: 0,
@@ -250,6 +262,7 @@
 const itemDataObject = ref()
 const timer = ref()
 let myChart = null;
+const isPlay = ref(false)
 
 const formRules = reactive({
   calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
@@ -258,7 +271,7 @@
 })
 
 /** 查询列表 */
-const getList = async () => {
+const getList = async (isClear = true) => {
   loading1.value = true
   try {
     if (!formData.value.chartCheck) {
@@ -284,6 +297,22 @@
     formData.value.endTime = data.endTime
 
     let xAxisData = data.categories;
+    let defaultYAxis = [
+      {
+        type: 'value',
+        name: "累计值",
+        splitLine: {show: false},
+        axisLine: {show: true},
+        position: 'right'
+      },
+      {
+        type: 'value',
+        name: "",
+        splitLine: {show: false},
+        axisLine: {show: true},
+        position: 'left'
+      }
+    ];
     let yAxisData = [];
     let offset = 0;
     let yAxisIndex = 0;
@@ -314,12 +343,27 @@
       },
     });
     itemDataObject.value = {}
+    yAxisData.push({
+      type: 'value',
+      name: "累计值",
+      position: 'right',
+      splitLine: {
+        show: false
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {}
+      },
+      axisLabel: {
+        formatter: '{value}'
+      }
+    })
     for (let i = 0; i < data.dataViewList.length; i++) {
       let dataView = data.dataViewList[i]
       itemDataObject.value[dataView.outId] = dataView;
       let maxValue = dataView.maxValue;
       let minValue = dataView.minValue;
-      yAxisIndex = formData.value.isMultipleY ? i : 0;
+      yAxisIndex = (formData.value.isMultipleY ? i : 0) + 1;
       let yMax = maxValue;
       if (maxValue < 0) {
         maxValue = 1;
@@ -369,7 +413,7 @@
           type: 'line',
           yAxisIndex: yAxisIndex,
           showSymbol: false,
-          smooth: true,
+          smooth: false,
           lineStyle: {
             width: 2
           }
@@ -377,13 +421,14 @@
       }
       if (chartCheckArray.indexOf('T+N') !== -1) {
         let legendName = dataView.resultName + '(T+N)';
+        legendData.push(legendName);
         seriesData.push({
           name: legendName,
           data: dataView.preDataN || [],
           type: 'line',
           yAxisIndex: yAxisIndex,
           showSymbol: false,
-          smooth: true,
+          smooth: false,
           lineStyle: {
             width: 2
           }
@@ -399,7 +444,7 @@
           showSymbol: false,
           connectNulls: true,
           yAxisIndex: yAxisIndex,
-          smooth: true,
+          smooth: false,
           lineStyle: {
             width: 2
           }
@@ -413,10 +458,10 @@
           data: dataView.curData || [],
           type: 'line',
           yAxisIndex: yAxisIndex,
-          showSymbol: false,
-          smooth: true,
+          showSymbol: true,
+          smooth: false,
           lineStyle: {
-            width: 2
+            width: 3
           }
         });
       }
@@ -430,7 +475,51 @@
           yAxisIndex: yAxisIndex,
           showSymbol: false,
           connectNulls: true,
-          smooth: true,
+          smooth: false,
+          lineStyle: {
+            width: 2,
+            type: 'dashed'
+          }
+        });
+      }
+
+      if (chartCheckArray.indexOf('预测累计') !== -1) {
+        let legendName = dataView.resultName + '(预测累计)';
+        legendData.push(legendName);
+        let seriesLeiJiData = []
+        if (dataView.cumulantPreData) {
+          seriesLeiJiData = dataView.cumulantPreData
+        }
+        seriesData.push({
+          name: legendName,
+          data: seriesLeiJiData,
+          type: 'line',
+          yAxisIndex: 0,
+          showSymbol: false,
+          connectNulls: true,
+          smooth: false,
+          lineStyle: {
+            width: 2,
+            type: 'dashed'
+          }
+        });
+      }
+
+      if (chartCheckArray.indexOf('真实累计') !== -1) {
+        let legendName = dataView.resultName + '(真实累计)';
+        legendData.push(legendName);
+        let seriesLeiJiData = []
+        if (dataView.cumulantRealData) {
+          seriesLeiJiData = dataView.cumulantRealData
+        }
+        seriesData.push({
+          name: legendName,
+          data: seriesLeiJiData,
+          type: 'line',
+          yAxisIndex: 0,
+          showSymbol: false,
+          connectNulls: true,
+          smooth: false,
           lineStyle: {
             width: 2,
             type: 'dashed'
@@ -448,6 +537,7 @@
         }
       }
     }
+
     myChart = echarts.init(dataAnalysisChart.value);
     let option = {
       title: {
@@ -473,11 +563,7 @@
         boundaryGap: false,
         data: xAxisData
       },
-      yAxis: formData.value.isMultipleY ? yAxisData : {
-        type: 'value',
-        splitLine: {show: false},
-        axisLine: {show: true}
-      },
+      yAxis: formData.value.isMultipleY ? yAxisData : defaultYAxis,
       dataZoom: [
         {
           type: 'inside',
@@ -491,11 +577,15 @@
       ],
       series: seriesData
     }
-    myChart.clear()
+    if (isClear) {
+      myChart.clear()
+    }
     myChart.setOption(option)
   } finally {
     loading1.value = false
   }
+
+  calItemBaseVale()
 }
 
 onMounted(() => {
@@ -507,24 +597,67 @@
   treeData.value = await McsApi.getPredictItemTree()
 }
 
+function changeChartCheck(value) {
+  getList(true)
+}
+
+function onChangeMultipleY() {
+  getList(true)
+}
+
+function playChart(isBack = false) {
+  let mins = isBack ? formData.value.predictFreq * -1 : formData.value.predictFreq
+  let startTime = formData.value.startTime;
+  let endTime = formData.value.endTime;
+  let predictTime = formData.value.predictTime;
+  if (predictTime) {
+    predictTime = getYMDHMS(new Date(predictTime).getTime() + 1000 * 60 * mins);
+    formData.value.predictTime = predictTime;
+  }
+  if (startTime) {
+    startTime = getYMDHMS(new Date(startTime).getTime() + 1000 * 60 * mins);
+    formData.value.startTime = startTime;
+  }
+  if (endTime) {
+    endTime = getYMDHMS(new Date(endTime).getTime() + 1000 * 60 * mins);
+    formData.value.endTime = endTime;
+  }
+  getList(false);
+}
+
+function playHandle(type) {
+  isPlay.value = 'play' === type
+  let doPlay = setInterval(function () {
+    if (isPlay.value) {
+      playChart()
+    } else {
+      clearInterval(doPlay);
+    }
+    if (new Date().getTime() - new Date(formData.value.predictTime).getTime() < 1000 * 60 ) {
+      isPlay.value = false
+      clearInterval(doPlay);
+    }
+  }, 1000)
+}
+
 function leftSearchDataByRange() {
   let mins = getRangeMins();
   let startTime = formData.value.startTime;
   let endTime = formData.value.endTime;
   let predictTime = formData.value.predictTime;
   if (predictTime) {
-    predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins);
+    predictTime = getYMDHMS(new Date(predictTime).getTime() - 1000 * 60 * mins);
     formData.value.predictTime = predictTime;
   }
   if (startTime) {
-    startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins);
+    startTime = getYMDHMS(new Date(startTime).getTime() - 1000 * 60 * mins);
     formData.value.startTime = startTime;
   }
   if (endTime) {
-    endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins);
+    endTime = getYMDHMS(new Date(endTime).getTime() - 1000 * 60 * mins);
     formData.value.endTime = endTime;
   }
-  getList();
+  getList(false);
 }
 
 function getRangeMins() {
@@ -541,7 +674,13 @@
 function onCheckTree(data, checked, indeterminate) {
   formData.value.checkedItemData = [];
   if (checked.checkedNodes) {
-    formData.value.checkedItemData = [...checked.checkedNodes]
+    let cns = [...checked.checkedNodes]
+    for (let i = 0; i < cns.length; i++) {
+      if (cns[i].id.indexOf('-') !== -1) {
+        continue
+      }
+      formData.value.checkedItemData.push(cns[i])
+    }
   }
   debounce(getList, 1000);
 }
@@ -578,60 +717,59 @@
     calRateForm.value.itemAvg = dataView.hisAvg;
     calRateForm.value.realCumulant = dataView.hisCumulant;
   }
+  calAccuracyRate()
 }
 
 function calAccuracyRate() {
-  this.$refs['calRateForm'].validate((valid) => {
-    if (!valid) {
-      return false
-    }
-    let dataView = itemDataObject[calRateForm.value.calItem]
-    let seriesReaData = dataView.realData;
-    let seriesPreData = dataView.preDataL;
-    if (seriesReaData == null || seriesPreData == null ||
-      seriesReaData.length === 0 || seriesPreData.length === 0) {
-      loading2.value = false;
-      return;
-    }
-    let predictValueMap = {};
-    seriesPreData.forEach(function (item) {
-      predictValueMap[item[0]] = item[1];
-    })
-    let pointValueMap = {};
-    seriesReaData.forEach(function (item) {
-      pointValueMap[item[0]] = item[1];
-    })
-    let inDeviation = Number(calRateForm.value.IN_DEVIATION);
-    let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
-    if (inDeviation === 0 && outDeviation === 0) {
-      loading2.value = false;
-      return;
-    }
-    let inDeviationCount = 0;
-    let outDeviationCount = 0;
-    let totalCount = 0;
-    for (let key in predictValueMap) {
-      let predictValue = predictValueMap[key];
-      let pointValue = pointValueMap[key];
-      if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
-        continue;
-      }
-      let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
-      if (deviationAbs < inDeviation) {
-        inDeviationCount = inDeviationCount + 1;
-      }
-      if (deviationAbs > outDeviation) {
-        outDeviationCount = outDeviationCount + 1;
-      }
-      totalCount = totalCount + 1;
-    }
+  const valid = calRateFormRef.value.validate()
+  if (!valid) return
 
-    let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
-    let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
-    calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
-    calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
+  let dataView = itemDataObject.value[calRateForm.value.calItem]
+  let seriesReaData = dataView.realData;
+  let seriesPreData = dataView.preDataL;
+  if (seriesReaData == null || seriesPreData == null ||
+    seriesReaData.length === 0 || seriesPreData.length === 0) {
     loading2.value = false;
+    return;
+  }
+  let predictValueMap = {};
+  seriesPreData.forEach(function (item) {
+    predictValueMap[item[0]] = item[1];
   })
+  let pointValueMap = {};
+  seriesReaData.forEach(function (item) {
+    pointValueMap[item[0]] = item[1];
+  })
+  let inDeviation = Number(calRateForm.value.IN_DEVIATION);
+  let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
+  if (inDeviation === 0 && outDeviation === 0) {
+    loading2.value = false;
+    return;
+  }
+  let inDeviationCount = 0;
+  let outDeviationCount = 0;
+  let totalCount = 0;
+  for (let key in predictValueMap) {
+    let predictValue = predictValueMap[key];
+    let pointValue = pointValueMap[key];
+    if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
+      continue;
+    }
+    let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
+    if (deviationAbs < inDeviation) {
+      inDeviationCount = inDeviationCount + 1;
+    }
+    if (deviationAbs > outDeviation) {
+      outDeviationCount = outDeviationCount + 1;
+    }
+    totalCount = totalCount + 1;
+  }
+
+  let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
+  let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
+  calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
+  calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
+  loading2.value = false;
 }
 
 function rightSearchDataByRange() {
@@ -640,18 +778,18 @@
   let endTime = formData.value.endTime;
   let predictTime = formData.value.predictTime;
   if (predictTime) {
-    predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins);
+    predictTime = getYMDHMS(new Date(predictTime).getTime() + 1000 * 60 * mins);
     formData.value.predictTime = predictTime;
   }
   if (startTime) {
-    startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins);
+    startTime = getYMDHMS(new Date(startTime).getTime() + 1000 * 60 * mins);
     formData.value.startTime = startTime;
   }
   if (endTime) {
-    endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins);
+    endTime = getYMDHMS(new Date(endTime).getTime() + 1000 * 60 * mins);
     formData.value.endTime = endTime;
   }
-  getList();
+  getList(false);
 }
 
 /** 重置表单 */
@@ -671,7 +809,7 @@
     currentStamp60: '',
     predictStamp: '',
     chartCheck: ['T+L', '真实值'],
-    chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'],
+    chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值', '预测累计', '真实累计'],
     checkedItemData: [],
     backItem: '',
     backValue: 0,
@@ -681,12 +819,12 @@
     queryStep: 2,
     isMultipleYRadio: '单坐标轴',
     isMultipleY: false,
-    predictFreq: 3,
+    predictFreq: 2,
   }
   calRateForm.value = {
     calItem: undefined,
-    IN_DEVIATION: 0,
-    OUT_DEVIATION: 0,
+    IN_DEVIATION: 10,
+    OUT_DEVIATION: 50,
     IN_ACCURACY_RATE: 0,
     OUT_ACCURACY_RATE: 0,
     itemAvg: 0,
diff --git a/src/views/model/pre/item/MmPredictItemChart.vue b/src/views/model/pre/item/MmPredictItemChart.vue
index 4d24c06..824de21 100644
--- a/src/views/model/pre/item/MmPredictItemChart.vue
+++ b/src/views/model/pre/item/MmPredictItemChart.vue
@@ -167,6 +167,39 @@
               },
             },
           })
+          //累计点
+          if(viewData.cumulantRealData) {
+            legendData.push(key + "累计:" + '真实值')
+            seriesData.push({
+              name: key + "累计:" + '真实值',
+              type: "line",
+              data: viewData.cumulantRealData,
+              showSymbol: false,
+              smooth: false,
+              lineStyle: {
+                normal: {
+                  width: 1,
+                },
+              },
+              yAxisIndex: 1
+            })
+          }
+          if(viewData.cumulantPreData) {
+            legendData.push(key + "累计:" + '预测值')
+            seriesData.push({
+              name: key + "累计:" + '预测值',
+              type: "line",
+              data: viewData.cumulantPreData,
+              showSymbol: false,
+              smooth: false,
+              lineStyle: {
+                normal: {
+                  width: 1,
+                },
+              },
+              yAxisIndex: 1
+            })
+          }
         })
       }
 
@@ -207,9 +240,19 @@
           boundaryGap: false,
           data: data.categories,
         },
-        yAxis: {
-          type: "value",
-        },
+        yAxis: [
+          {
+            type: "value",
+            name: '预测值/真实值'
+          },
+          {
+            type: "value",
+            splitLine: {
+              show: false
+            },
+            name: '累计值'
+          }
+        ],
         dataZoom: [
           {
             type: "inside",
diff --git a/src/views/model/pre/item/MmPredictItemForm.vue b/src/views/model/pre/item/MmPredictItemForm.vue
index fc87750..a583075 100644
--- a/src/views/model/pre/item/MmPredictItemForm.vue
+++ b/src/views/model/pre/item/MmPredictItemForm.vue
@@ -1,5 +1,5 @@
 <template>
-  <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%">
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="75%">
     <el-form
       ref="formRef"
       v-loading="formLoading"
@@ -101,31 +101,69 @@
           </el-form-item>
         </el-col>
       </el-row>
-      <el-row v-if="dataForm.itemtypename === 'MergeItem'">
-        <el-col :span="12">
-          <el-form-item label="预测长度" prop="mmPredictItem.predictlength">
-            <el-input
-              @change="clearExpressionList"
-              v-model="dataForm.mmPredictItem.predictlength" placeholder="预测长度"
-              maxlength="5"/>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="真实数据点">
-            <el-select
-              v-model="dataForm.pointId"
-              filterable
-              clearable
-              placeholder="请选择">
-              <el-option
-                v-for="item in pointList"
-                :key="item.id"
-                :label="item.pointName"
-                :value="item.id"/>
-            </el-select>
-          </el-form-item>
-        </el-col>
-      </el-row>
+      <div v-if="dataForm.itemtypename === 'MergeItem'">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="预测长度" prop="mmPredictItem.predictlength">
+              <el-input
+                v-model="dataForm.mmPredictItem.predictlength" placeholder="预测长度"
+                maxlength="5"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="真实数据点">
+              <el-select
+                v-model="dataForm.pointId"
+                filterable
+                clearable
+                placeholder="请选择">
+                <el-option
+                  v-for="item in pointList"
+                  :key="item.id"
+                  :label="item.pointName"
+                  :value="item.id"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-divider content-position="left">累计配置</el-divider>
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="是否累计" prop="iscumulant">
+              <el-select v-model="dataForm.iscumulant" placeholder="请选择" @change="(value) => iscumulantChange(dataForm)">
+                <el-option
+                  v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="dict.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="累计除数" prop="cumuldivisor">
+              <el-input-number v-model="dataForm.cumuldivisor" style="width: 100%" :disabled="dataForm.iscumulant !== 1"
+                :min="1" :max="60"
+                :controls="false"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="累计测点">
+              <el-select
+                v-model="dataForm.cumulpoint" :disabled="dataForm.iscumulant !== 1"
+                filterable
+                clearable
+                placeholder="请选择">
+                <el-option
+                  v-for="item in pointList.filter(e => e.pointType === 'CUMULATE')"
+                  :key="item.id"
+                  :label="item.pointName"
+                  :value="item.id"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </div>
       <el-divider content-position="left" v-if="dataForm.itemtypename === 'NormalItem'">模型信息
       </el-divider>
       <el-row :gutter="8" v-if="dataForm.itemtypename === 'NormalItem'">
@@ -275,6 +313,41 @@
             </el-select>
           </template>
         </el-table-column>
+        <el-table-column label="是否累计" align="center" width="150px">
+          <template #default="scope">
+            <el-select v-model="scope.row.iscumulant" placeholder="请选择" @change="(value) => iscumulantChange(scope.row)">
+              <el-option
+                v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="累计除数" align="center" width="100px">
+          <template #default="scope">
+            <el-input-number v-model="scope.row.cumuldivisor" style="width: 100%"
+              :min="1" :max="60" :disabled="scope.row.iscumulant !== 1"
+              :controls="false"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="累计数据点" align="center">
+          <template #default="scope">
+            <el-select
+              v-model="scope.row.cumulpoint"
+              :disabled="scope.row.iscumulant !== 1"
+              filterable
+              clearable
+              placeholder="请选择">
+              <el-option
+                v-for="item in pointList.filter(e => e.pointType === 'CUMULATE')"
+                :key="item.id"
+                :label="item.pointName"
+                :value="item.id"/>
+            </el-select>
+          </template>
+        </el-table-column>
         <el-table-column prop="" label="操作" width="80" align="center">
           <template #default="scope">
             <el-button
@@ -399,11 +472,11 @@
               v-model="scope.row.point"
               placeholder="请选择"
               filterable
-              :no-data-text="'无数据(预测长度:' + dataForm.mmPredictItem.predictlength + ';管网:' + moduleList.find(e => e.id === dataForm.dmModuleItem.moduleid)?.modulename + ')'"
+              :no-data-text="'无数据(管网:' + moduleList.find(e => e.id === dataForm.dmModuleItem.moduleid)?.modulename + ')'"
               @change="changeNormalItemSelect"
               style="width: 100%">
               <el-option-group
-                v-for="group in modelparamListMap['NormalItem'].filter(e => e.predictlength == dataForm.mmPredictItem.predictlength && e.moduleid === dataForm.dmModuleItem.moduleid)"
+                v-for="group in modelparamListMap['NormalItem'].filter(e => e.moduleid === dataForm.dmModuleItem.moduleid)"
                 :key="group.value"
                 :label="group.label"
               >
@@ -472,6 +545,7 @@
 import * as DaPoint from '@/api/data/da/point'
 import {useUpload} from '@/api/model/pre/item'
 import * as ScheduleModelApi from '@/api/model/sche/model'
+import {getPointSimpleList} from "@/api/data/da/point";
 
 const {uploadUrl, httpRequest} = useUpload()
 
@@ -518,7 +592,7 @@
     predictphase: undefined,
     workchecked: 0,
     unittransfactor: undefined,
-    saveindex: undefined
+    saveindex: undefined,
   },
   dmModuleItem: {
     id: undefined,
@@ -558,7 +632,10 @@
   },
   mmModelArithSettingsList: [],
   mmModelParamList: [],
-  pointId: undefined
+  pointId: undefined,
+  iscumulant: 0,
+  cumuldivisor: undefined,
+  cumulpoint: undefined,
 })
 const formRules = reactive({
   'mmPredictItem.itemname': [{required: true, message: '预测项名不能为空', trigger: 'blur'}],
@@ -574,6 +651,7 @@
     trigger: 'blur'
   }],
   'mmPredictItem.status': [{required: true, message: '是否启用不能为空', trigger: 'blur'}],
+  'iscumulant': [{required: true, message: '是否累计不能为空', trigger: 'blur'}],
   'dmModuleItem.moduleid': [{required: true, message: '管网不能为空', trigger: 'blur'}],
   'dmModuleItem.itemorder': [{required: true, message: '排序不能为空', trigger: 'blur'}],
   'mmPredictItem.predictlength': [{required: true, message: '预测长度不能为空', trigger: 'blur'}],
@@ -611,7 +689,7 @@
   mpkProjectList.value = await ProjectApi.list()
 
   // 获取数据点列表
-  pointNoList.value = await DaPoint.getPointList(queryParams)
+  pointNoList.value = await DaPoint.getPointSimpleList(queryParams)
   if (pointNoList.value.length > 0) {
     pointList.value = []
     pointNoList.value.forEach(function (value) {
@@ -640,6 +718,13 @@
   if (!formRef) return
   const valid = await formRef.value.validate()
   if (!valid) return
+  //校验累计配置
+  if (dataForm.value.iscumulant === 1) {
+    if (dataForm.value.cumuldivisor == undefined) {
+      message.error("累计除数不为空")
+      return
+    }
+  }
   //校验模型输出
   if (dataForm.value.itemtypename === 'NormalItem') {
     if (dataForm.value.mmItemOutputList == undefined || dataForm.value.mmItemOutputList.length <= 0) {
@@ -647,15 +732,30 @@
       return
     }
 
-    let flag = false
     dataForm.value.mmItemOutputList.forEach(e => {
-      if (e.resultstr == undefined || e.resultstr === '' || e.resultType == undefined || e.resultType === '' || (e.resultType === 2 && (e.resultIndex == undefined || e.resultIndex === ''))) {
-        message.error("模型输出数据异常")
-        flag = true
-        return
+      if (e.resultstr == undefined || e.resultstr === '' || e.resultType == undefined || e.resultType === ''
+        || (e.resultType === 2 && (e.resultIndex == undefined || e.resultIndex === ''))
+        || (e.iscumulant === 1 && e.cumuldivisor == undefined)
+      ) {
+        message.error("输出数据异常")
+        throw new Error('输出数据异常');
       }
     })
-    if (flag) return
+
+    //校验模型输入
+    dataForm.value.mmModelParamList.forEach(e => {
+      if (e.modelparamid == undefined || e.modelparamid == '') {
+        message.error("输入数据异常")
+        throw new Error('输入数据异常');
+      }
+      // ind_ascii类型输出的序号必须是1,且所在端口序号最大为1(一个ind_ascii类型输入独占一个端口)
+      if (e.modelparamtype === 'IND_ASCII') {
+        if (e.modelparamorder != 1 || dataForm.value.mmModelParamList.filter(p => p.modelparamportorder === e.modelparamportorder).length != 1) {
+          message.error("输入数据异常:IND_ASCII类型输入独占一个端口")
+          throw new Error('输入数据异常:IND_ASCII类型输入独占一个端口');
+        }
+      }
+    })
   }
   if (dataForm.value.itemtypename === 'MergeItem') {
     if (expressionList.value == undefined || expressionList.value.length <= 1) {
@@ -665,7 +765,7 @@
 
     let flag = false
     expressionList.value.forEach((e,index) => {
-      if (e.point == undefined || e.point === '' || e.operator == undefined || (e.operator === '' && index != expressionList.value.length - 1)) {
+      if (e.point == undefined || e.point === '' || ((e.operator == undefined || e.operator === '') && index != expressionList.value.length - 1)) {
         message.error("表达式数据异常")
         flag = true
         return
@@ -673,7 +773,6 @@
     })
     if (flag) return
   }
-
 
   // 提交请求
   formLoading.value = true
@@ -897,6 +996,10 @@
     row.resultIndex = 0
   }
 }
+function iscumulantChange(row) {
+  row.cumuldivisor = undefined
+  row.cumulpoint = undefined
+}
 
 function orderRow(rows) {
   let modelparamorder = 0
@@ -957,7 +1060,7 @@
       predictphase: '',
       workchecked: 0,
       unittransfactor: '',
-      saveindex: ''
+      saveindex: '',
     },
     dmModuleItem: {
       id: '',
@@ -993,7 +1096,10 @@
       itemid: '',
       expression: '',
       num: ''
-    }
+    },
+    iscumulant: 0,
+    cumuldivisor: undefined,
+    cumulpoint: '',
   }
   formRef.value?.resetFields()
 }
diff --git a/src/views/model/sche/model/ScheduleModelForm.vue b/src/views/model/sche/model/ScheduleModelForm.vue
index 397123b..5cdf9e9 100644
--- a/src/views/model/sche/model/ScheduleModelForm.vue
+++ b/src/views/model/sche/model/ScheduleModelForm.vue
@@ -103,7 +103,7 @@
           width="100"
           align="center">
           <template #default="scope">
-            <el-input v-model="scope.row.modelparamportorder" maxlength="5" clearable :disabled="true"
+            <el-input v-model="scope.row.modelparamportorder" maxlength="5" clearable
                       style="width:100%; hight:100%"/>
           </template>
         </el-table-column>
@@ -353,6 +353,8 @@
   import { CommonStatusEnum } from '@/utils/constants'
   import * as MpkApi from "@/api/model/mpk/mpk";
   import {generateUUID} from "@/utils";
+  import { ElMessage,ElMessageBox } from 'element-plus'
+  import { Refresh } from '@element-plus/icons-vue'
 
   defineOptions({ name: 'ScheduleModelForm' })
 
@@ -375,7 +377,13 @@
     resultStrId: undefined,
     invocation: undefined,
     status: CommonStatusEnum.ENABLE,
-    paramList: [],
+    paramList: [{
+      modelparamportorder: 1 + '',
+      modelparamorder: '1',
+      modelparamtype: '',
+      modelparamid: '',
+      datalength: 0
+    }],
     settingList: [],
     modelOut: []
   })
@@ -448,6 +456,21 @@
     if (!formRef) return
     const valid = await formRef.value.validate()
     if (!valid) return
+    //校验模型输入
+    formData.value.paramList.forEach(e => {
+      if (e.modelparamid == undefined || e.modelparamid == '') {
+        message.error("输入数据异常")
+        throw new Error('输入数据异常');
+      }
+      // ind_ascii类型输出的序号必须是1,且所在端口序号最大为1(一个ind_ascii类型输入独占一个端口)
+      if (e.modelparamtype === 'IND_ASCII') {
+        if (e.modelparamorder != 1 || formData.value.paramList.filter(p => p.modelparamportorder === e.modelparamportorder).length != 1) {
+          message.error("输入数据异常:IND_ASCII类型输入独占一个端口")
+          throw new Error('输入数据异常:IND_ASCII类型输入独占一个端口');
+        }
+      }
+    })
+
     // 提交请求
     formLoading.value = true
     try {
@@ -482,7 +505,13 @@
       resultStrId: undefined,
       invocation: undefined,
       status: CommonStatusEnum.ENABLE,
-      paramList: [],
+      paramList: [{
+        modelparamportorder: 1 + '',
+        modelparamorder: '1',
+        modelparamtype: '',
+        modelparamid: '',
+        datalength: 0
+      }],
       settingList: [],
       modelOut: []
     }
@@ -511,23 +540,39 @@
   const changeModel = async () => {
     // 校验
     if (model.value && model.value.length > 0) {
-      const modelInfo = model.value[0]
-      const methodInfo = model.value[1]
-      formData.value.modelName = modelInfo.pyChineseName
-      formData.value.className = modelInfo.pkgName + '.impl.' + modelInfo.pyName + 'Impl';
-      formData.value.methodName = methodInfo.methodName
-      formData.value.portLength = methodInfo.dataLength
-      // 参数构造
-      let paramStructure = []
-      for (let i = 0; i < methodInfo.dataLength; i++) {
-        paramStructure.push('[[D')
-      }
-      if (methodInfo.model === 1) {
-        paramStructure.push('java.util.HashMap')
-      }
+      ElMessageBox.confirm(
+        '是否更新输入参数?',
+        '提示',
+        {confirmButtonText: '是', cancelButtonText: '否', type: 'success',icon: markRaw(Refresh),closeOnClickModal:false,closeOnPressEscape:false}
+      ).then(() => {
+        relevanceModel(true)
+      }).catch(() => {
+        relevanceModel(false)
+      })
+    }else {
+      message.error("请先选择模型")
+    }
+  }
+
+  function relevanceModel(refreshParam) {
+    const modelInfo = model.value[0]
+    const methodInfo = model.value[1]
+    formData.value.modelName = modelInfo.pyChineseName
+    formData.value.className = modelInfo.pkgName + '.impl.' + modelInfo.pyName + 'Impl';
+    formData.value.methodName = methodInfo.methodName
+    formData.value.portLength = methodInfo.dataLength
+    // 参数构造
+    let paramStructure = []
+    for (let i = 0; i < methodInfo.dataLength; i++) {
+      paramStructure.push('[[D')
+    }
+    if (methodInfo.model === 1) {
       paramStructure.push('java.util.HashMap')
-      formData.value.paramStructure = paramStructure.join(',')
-      formData.value.modelPath = modelInfo.pyModule
+    }
+    paramStructure.push('java.util.HashMap')
+    formData.value.paramStructure = paramStructure.join(',')
+    formData.value.modelPath = modelInfo.pyModule
+    if (refreshParam) {
       // 输入参数
       let paramList = []
       for (let i = 0; i < methodInfo.dataLength; i++) {
@@ -539,23 +584,20 @@
           datalength: 0
         })
       }
-
       formData.value.paramList = paramList
-      // 设置参数
-      let settingList = []
-      methodInfo.methodSettings.forEach(e => {
-        settingList.push({
-          key: e.settingKey,
-          value: e.value,
-          valuetype: e.valueType,
-          name: e.name
-        })
-      })
-      formData.value.settingList = settingList
-      modelPopover.value.hide()
-    }else {
-      message.error("请先选择模型")
     }
+    // 设置参数
+    let settingList = []
+    methodInfo.methodSettings.forEach(e => {
+      settingList.push({
+        key: e.settingKey,
+        value: e.value,
+        valuetype: e.valueType,
+        name: e.name
+      })
+    })
+    formData.value.settingList = settingList
+    modelPopover.value.hide()
   }
 
   function changeModelparamtype(row) {
diff --git a/src/views/model/sche/scheme/record/index.vue b/src/views/model/sche/scheme/record/index.vue
index 4d74cda..5175503 100644
--- a/src/views/model/sche/scheme/record/index.vue
+++ b/src/views/model/sche/scheme/record/index.vue
@@ -47,7 +47,7 @@
     </ContentWrap>
     <!-- 列表 -->
     <ContentWrap>
-      <el-table v-loading="loading" :data="list">
+      <el-table v-loading="loading" :data="list" max-height="300px">
         <el-table-column
           prop="scheduleTime"
           label="调度时间"
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="登录日期">
diff --git a/src/views/system/loginlog/index.vue b/src/views/system/loginlog/index.vue
index 011992b..4c442b8 100644
--- a/src/views/system/loginlog/index.vue
+++ b/src/views/system/loginlog/index.vue
@@ -45,7 +45,7 @@
           plain
           @click="handleExport"
           :loading="exportLoading"
-          v-hasPermi="['infra:login-log:export']"
+          v-hasPermi="['system:login-log:export']"
         >
           <Icon icon="ep:download" class="mr-5px" /> 导出
         </el-button>
@@ -56,16 +56,16 @@
   <!-- 列表 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list">
-      <el-table-column label="日志编号" align="center" prop="id" />
-      <el-table-column label="操作类型" align="center" prop="logType">
+      <el-table-column label="日志编号" align="center" prop="id" width="100" />
+      <el-table-column label="操作类型" align="center" prop="logType" width="100">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_TYPE" :value="scope.row.logType" />
         </template>
       </el-table-column>
       <el-table-column label="用户名称" align="center" prop="username" width="180" />
       <el-table-column label="登录地址" align="center" prop="userIp" width="180" />
-      <el-table-column label="浏览器" align="center" prop="userAgent" />
-      <el-table-column label="登陆结果" align="center" prop="result">
+      <el-table-column label="浏览器" align="center" prop="userAgent" :show-overflow-tooltip="true"/>
+      <el-table-column label="登录结果" align="center" prop="result" width="100">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="scope.row.result" />
         </template>
@@ -77,13 +77,13 @@
         width="180"
         :formatter="dateFormatter"
       />
-      <el-table-column label="操作" align="center">
+      <el-table-column label="操作" align="center" width="80">
         <template #default="scope">
           <el-button
             link
             type="primary"
             @click="openDetail(scope.row)"
-            v-hasPermi="['infra:login-log:query']"
+            v-hasPermi="['system:login-log:query']"
           >
             详情
           </el-button>
diff --git a/src/views/system/operatelog/index.vue b/src/views/system/operatelog/index.vue
index bd67305..dc19764 100644
--- a/src/views/system/operatelog/index.vue
+++ b/src/views/system/operatelog/index.vue
@@ -79,7 +79,7 @@
           plain
           @click="handleExport"
           :loading="exportLoading"
-          v-hasPermi="['infra:operate-log:export']"
+          v-hasPermi="['system:operate-log:export']"
         >
           <Icon icon="ep:download" class="mr-5px" /> 导出
         </el-button>
@@ -110,7 +110,7 @@
             link
             type="primary"
             @click="openDetail(scope.row)"
-            v-hasPermi="['infra:operate-log:query']"
+            v-hasPermi="['system:operate-log:query']"
           >
             详情
           </el-button>

--
Gitblit v1.9.3