From 4a859a6d69984c77fa8166255c65f5a94eb0bd71 Mon Sep 17 00:00:00 2001
From: 潘志宝 <979469083@qq.com>
Date: 星期五, 28 二月 2025 17:34:55 +0800
Subject: [PATCH] Merge branch 'master' of http://dlindusit.com:53929/r/iailab-plat-ui-vue3

---
 src/views/data/ind/item/CalIndDefineForm.vue                |   23 
 src/views/data/ind/item/IndHistoryChart.vue                 |  169 +++++
 src/views/model/matlab/model/index.vue                      |  188 +++++
 src/views/model/mpk/file/MpkRun.vue                         |   61 +
 src/views/data/ind/item/IndCurrentData.vue                  |    2 
 src/views/data/ind/item/AtomIndDefineForm.vue               |   17 
 src/views/model/matlab/model/MatlabModelForm.vue            |  394 +++++++++++
 src/views/model/matlab/project/MatlabProjectModelDialog.vue |  101 +++
 src/utils/download.ts                                       |    5 
 src/api/model/matlab/project.ts                             |   33 +
 src/views/data/ind/item/index.vue                           |   17 
 src/views/model/matlab/model/MatlabModelSettingForm.vue     |  144 ++++
 src/views/model/mpk/file/index.vue                          |   17 
 src/router/modules/remaining.ts                             |   23 
 src/api/data/ind/item/item.ts                               |    9 
 src/views/data/ind/item/DerIndDefineForm.vue                |   17 
 src/views/model/matlab/project/MatlabProjectForm.vue        |  156 ++++
 src/api/model/matlab/mlModel.ts                             |   29 
 src/views/model/pre/item/MmPredictItemForm.vue              |   23 
 src/views/model/matlab/model/MatlabRun.vue                  |  279 ++++++++
 src/views/model/matlab/project/index.vue                    |  221 ++++++
 src/utils/dict.ts                                           |    5 
 src/api/model/sche/model/index.ts                           |    1 
 src/views/model/sche/model/ScheduleModelForm.vue            |   15 
 src/api/model/mpk/mpk.ts                                    |    4 
 25 files changed, 1,928 insertions(+), 25 deletions(-)

diff --git a/src/api/data/ind/item/item.ts b/src/api/data/ind/item/item.ts
index ed09f65..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) => {
@@ -68,3 +73,7 @@
 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/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/sche/model/index.ts b/src/api/model/sche/model/index.ts
index 51a9ce7..39a1184 100644
--- a/src/api/model/sche/model/index.ts
+++ b/src/api/model/sche/model/index.ts
@@ -165,5 +165,6 @@
     'MergeItem': mergeItemList,
     'PLAN': planList,
     'IND': indList,
+    'IND_ASCII': indList,
   }
 }
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/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/ind/item/AtomIndDefineForm.vue b/src/views/data/ind/item/AtomIndDefineForm.vue
index 97f2e34..6368c02 100644
--- a/src/views/data/ind/item/AtomIndDefineForm.vue
+++ b/src/views/data/ind/item/AtomIndDefineForm.vue
@@ -59,6 +59,22 @@
             <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="12">
@@ -143,6 +159,7 @@
     timeRange: '',
     timeGranularity: '',
     remark: '',
+    solidifyFlag: '',
     atomItem:{
       dataSource:'',
       dataSet: '',
diff --git a/src/views/data/ind/item/CalIndDefineForm.vue b/src/views/data/ind/item/CalIndDefineForm.vue
index 52d78cc..28a1235 100644
--- a/src/views/data/ind/item/CalIndDefineForm.vue
+++ b/src/views/data/ind/item/CalIndDefineForm.vue
@@ -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>
@@ -163,6 +179,7 @@
     timeRange: '',
     timeGranularity: '',
     remark: '',
+    solidifyFlag:'',
     calItem: {
       id: '',
       expression: '',
diff --git a/src/views/data/ind/item/DerIndDefineForm.vue b/src/views/data/ind/item/DerIndDefineForm.vue
index dc35975..eccf7c0 100644
--- a/src/views/data/ind/item/DerIndDefineForm.vue
+++ b/src/views/data/ind/item/DerIndDefineForm.vue
@@ -80,6 +80,22 @@
             <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">
@@ -188,6 +204,7 @@
     businessType: '',
     timeRange: '',
     timeGranularity: '',
+    solidifyFlag:'',
     atomItem: {
       id: '',
       itemId: '',
diff --git a/src/views/data/ind/item/IndCurrentData.vue b/src/views/data/ind/item/IndCurrentData.vue
index 629cdec..624ca10 100644
--- a/src/views/data/ind/item/IndCurrentData.vue
+++ b/src/views/data/ind/item/IndCurrentData.vue
@@ -40,6 +40,6 @@
 defineExpose({open}) // 提供 open 方法,用于打开弹窗
 
 const getData = async() =>{
-  dataForm.value.itemCurrentData = await ItemApi.getItemCurrentData(dataForm.value.itemNo);
+  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 f677122..3a3c9a9 100644
--- a/src/views/data/ind/item/index.vue
+++ b/src/views/data/ind/item/index.vue
@@ -91,11 +91,16 @@
                 修改
               </el-button>
               <el-button
-                v-hasPermi="['data:ind-item:update']"
                 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']"
@@ -124,7 +129,8 @@
   <DerIndDefineForm ref="derFormRef" @success="getList" />
   <CalIndDefineForm ref="calFormRef" @success="getList" />
   <SelectItemType ref="itemTypeSel"/>
-  <IndCurrentData ref="indCurrentData" />
+  <IndCurrentData ref="indCurrentData"/>
+  <IndHistoryChart ref="indHistoryChart"/>
 </template>
 
 <script lang="ts" setup>
@@ -139,6 +145,7 @@
   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";
 
 
@@ -219,11 +226,15 @@
     }
   }
   const indCurrentData = ref()
-
   const getCurrentData = (itemNo: string) => {
     indCurrentData.value.open(itemNo)
   }
 
+  const indHistoryChart = ref()
+  const getHistoryData = (raw: object) => {
+    indHistoryChart.value.open(raw)
+  }
+
   /** 删除按钮操作 */
   const handleDelete = async (id: number) => {
     try {
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/item/MmPredictItemForm.vue b/src/views/model/pre/item/MmPredictItemForm.vue
index 52eae1e..a583075 100644
--- a/src/views/model/pre/item/MmPredictItemForm.vue
+++ b/src/views/model/pre/item/MmPredictItemForm.vue
@@ -732,18 +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 === ''))
         || (e.iscumulant === 1 && e.cumuldivisor == undefined)
       ) {
-        message.error("模型输出数据异常")
-        flag = true
-        return
+        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) {
@@ -761,7 +773,6 @@
     })
     if (flag) return
   }
-
 
   // 提交请求
   formLoading.value = true
diff --git a/src/views/model/sche/model/ScheduleModelForm.vue b/src/views/model/sche/model/ScheduleModelForm.vue
index 90a536d..5cdf9e9 100644
--- a/src/views/model/sche/model/ScheduleModelForm.vue
+++ b/src/views/model/sche/model/ScheduleModelForm.vue
@@ -456,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 {

--
Gitblit v1.9.3