Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/api/_prefix.js
#	src/components/Sidebar.vue
#	src/views/file/FileList.vue
chillqi 3 years ago
parent
commit
cfdd70a2aa

+ 14 - 4
package-lock.json

@@ -1252,12 +1252,22 @@
       "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
     },
     "echarts": {
-      "version": "5.4.1",
-      "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.1.tgz",
-      "integrity": "sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==",
+      "version": "5.4.2",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.2.tgz",
+      "integrity": "sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==",
       "requires": {
         "tslib": "2.3.0",
-        "zrender": "5.4.1"
+        "zrender": "5.4.3"
+      },
+      "dependencies": {
+        "zrender": {
+          "version": "5.4.3",
+          "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.3.tgz",
+          "integrity": "sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==",
+          "requires": {
+            "tslib": "2.3.0"
+          }
+        }
       }
     },
     "echarts-gl": {

+ 1 - 1
package.json

@@ -10,7 +10,7 @@
   "dependencies": {
     "@vitejs/plugin-vue": "^1.10.2",
     "axios": "^0.21.1",
-    "echarts": "^5.3.2",
+    "echarts": "^5.4.2",
     "echarts-gl": "^2.0.9",
     "element-plus": "1.0.2-beta.52",
     "vue": "^3.1.2",

+ 10 - 1
src/App.vue

@@ -3,7 +3,16 @@
 </template>
 
 <script>
-export default {};
+import * as echarts from 'echarts'
+import { provide } from 'vue'
+export default {
+  name: 'App',
+  setup(){
+    provide('echarts',echarts)               //provide
+  },
+  components: {
+  }
+};
 </script>
 
 <style>

+ 20 - 4
src/api/_prefix.js

@@ -1,12 +1,28 @@
+//总api模块
 export const API_MODULE = '/api'
 export const NEW_API_MODULE= '/newapi'
-export const MODEL_MODULE=`${API_MODULE}/model`
-export const CONFIG_MODULE = `${API_MODULE}/config`
-export const FILE_MODULE= `${API_MODULE}/file`
+
+//文件模块
+export const FILE_MODULE= `${API_MODULE}/files`
+export const SQUARE_MODULE= `${NEW_API_MODULE}/square`
+//ml模块
+export const ML_MODULE= `${API_MODULE}/ml`
+export const CONFIG_MODULE = `${ML_MODULE}/configs`
+export const MODEL_MODULE=`${ML_MODULE}/models`
+export const PREDICTION_MODULE=`${ML_MODULE}/predictions`
+export const DEPLOYMENT_MODULE=`${ML_MODULE}/deployments`
+export const PIPELINE_MODULE=`${ML_MODULE}/pipelines`
+//analysis模块
+export const ANALYSIS_MODULE = `${API_MODULE}/analysis`
+export const ANALYSIS_CONFIG_MODULE = `${ANALYSIS_MODULE}/configs`
+export const ANALYSIS_VISUALIZE_MODULE = `${ANALYSIS_MODULE}/visualize`
+export const ANALYSIS_PIPELINE_MODULE = `${ANALYSIS_MODULE}/pipelines`
+
+//待整理
 export const PIC_MODULE= `${API_MODULE}/picture`
 export const POST_MODULE=`${NEW_API_MODULE}/posts`
 export const COMMENT_MODULE=`${NEW_API_MODULE}/comment`
 export const MAIL_MODULE=`${NEW_API_MODULE}/mail`
-export const SQUARE_MODULE= `${NEW_API_MODULE}/square`
+export const MOCK=`https://www.fastmock.site/mock/bf3861ab0686afc109db950891639c79/api`
 
 

+ 28 - 0
src/api/analysis.js

@@ -0,0 +1,28 @@
+import {ANALYSIS_MODULE, MOCK} from "./_prefix";
+import {axios} from '../utils/request'
+
+/**
+ *  添加配置信息 POST
+ * @param {*} payload
+ * @returns
+ */
+export const visualize = async payload => {
+    const { userId, configId, func} = payload;
+    // return await axios.post(`${MOCK}`, {
+    return await axios.post(`${MOCK}/analysis/visualize/:configId`, {
+        userId, configId, func
+    }).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 获取一个配置下的所有分析结果 GET
+ * @param userId
+ * @returns
+ */
+export const getAnalysisByConfig = async configId => {
+    // const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
+    const res = await axios.get(`${MOCK}/analysis/visualize/{configId}`)
+    return res
+}

+ 56 - 0
src/api/analysisConfig.js

@@ -0,0 +1,56 @@
+import {ANALYSIS_CONFIG_MODULE, MOCK} from "./_prefix";
+import {axios} from '../utils/request'
+
+/**
+ *  添加配置信息 POST
+ * @param {*} payload
+ * @returns
+ */
+export const addAnalysisConfig = payload => {
+    const { fileId, userId, configName, chosenColumns} = payload;
+    // return axios.post(`${MOCK}/ml/configs`, {
+    return axios.post(`${MOCK}/analysis/configs`, {
+        fileId, userId, configName, chosenColumns
+    }).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 获取所有配置 GET  传入用户id
+ * @param {userId} payload
+ * @returns
+ */
+export const getAnalysisConfig = payload => {
+    const {uid} = payload;
+    // return axios.get(`${ANALYSIS_CONFIG_MODULE}?userId=${uid}`).then(res => {
+        return axios.get(`${MOCK}/analysis/configs/all/:userId`).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 删除配置
+ * @param {*} payload
+ * @returns
+ */
+export const deleteAnalysisConfig = payload => {
+    const {configId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/delete?configId=${configId}`).then(res => {
+    return axios.delete(`${MOCK}/ml/configs/${configId}`).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 获取单个个配置 GET
+ * @param {*} payload
+ * @returns
+ */
+export const getOneAnalysisConfig = payload => {
+    const { configId } = payload;
+    // return axios.get(`${CONFIG_MODULE}/get/one/config?configId=${configId}`).then(res => {
+    //   return res;});
+    return axios.get(`${MOCK}/analysis/configs/${configId}`).then(res => {
+        return res;});
+}

+ 59 - 0
src/api/analysisPipeline.js

@@ -0,0 +1,59 @@
+import {axios} from '../utils/request'
+import {ANALYSIS_PIPELINE_MODULE} from "./_prefix";
+import {MOCK} from "./_prefix";
+
+/**
+ * 新建流水线 POST
+ * @param {*} payload
+ * @returns
+ */
+export const addAnalysisPipeline = async payload => {
+    const {userId, pipelineName, analysisFunction, chosenColumns } = payload;
+    const res = await axios.post(`${MOCK}/analysis/pipelines`, {
+        userId,
+        pipelineName,
+        analysisFunction,
+        chosenColumns
+    })
+    return res
+}
+
+/**
+ * 获取所有流水线
+ * @param {*} payload
+ * @returns
+ */
+export const getAllAnalysisPipeline = payload => {
+    const {userId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
+    //   return res;
+    return axios.get(`${MOCK}/analysis/pipelines/all/:userId`).then(res => {
+        return res;
+    });
+}
+/**
+ * 使用水线
+ * @param {*} payload
+ * @returns
+ */
+export const useAnalysisPipeline = payload => {
+    const {pipelineId, fileId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
+    //   return res;
+    return axios.post(`${MOCK}/analysis/pipelines/:pipelineId`).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 删除流水线 GET
+ * @param {*} payload
+ * @returns
+ */
+export const deleteAnalysisPipeline = payload => {
+    const {pipelineId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/delete?configId=${configId}`).then(res => {
+    return axios.delete(`${MOCK}/ml/pipelines/:pipelineId`).then(res => {
+        return res;
+    });
+}

+ 23 - 26
src/api/config.js

@@ -2,20 +2,22 @@ import request from '../utils/request';
 //import axios from 'axios';
 import {CONFIG_MODULE} from "./_prefix";
 import {axios} from '../utils/request'
-
+import {MOCK} from "./_prefix";
 
 /**
- *  添加模型配置信息 POST /add
+ *  添加模型配置信息 POST
  * @param {*} payload
  * @returns
  */
 export const addConfig = payload => {
-  console.log(payload);
-  //id,
-  const { fileInfoId,userId, modelTypeId,confName , fieldIds} = payload;
-
-  return axios.post(`${CONFIG_MODULE}/add`, {
-    fileInfoId, userId,modelTypeId,confName ,fieldIds
+  const { fileInfoId, userId, learningType, configName, features, label} = payload;
+  // return axios.post(`${CONFIG_MODULE}`, {
+  //   fileInfoId, userId, learningType ,configName ,features, label
+  // }).then(res => {
+  //   return res;
+  // });
+  return axios.post(`${MOCK}/ml/configs`, {
+    fileInfoId, userId, learningType ,configName ,features, label
   }).then(res => {
     return res;
   });
@@ -29,13 +31,11 @@ export const addConfig = payload => {
  * @returns
  */
 export const getAllConfig = payload => {
-  // console.log("getAllConfig",payload);
   const {uid} = payload;
-  console.log("payload",payload);
-  console.log("uid",uid);
-  return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
-
-    return res;
+  // return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
+  //   return res;
+    return axios.get(`${MOCK}/ml/configs:useId=1`).then(res => {
+      return res;
   });
 }
 
@@ -62,12 +62,10 @@ export const getAllNew = payload => {
  */
 export const getOneConfig = payload => {
   const { configId } = payload;
-  console.log("configId",configId);
-  console.log("payload",payload);
-  return axios.get(`${CONFIG_MODULE}/get/one/config?configId=${configId}`).then(res => {
-
+  // return axios.get(`${CONFIG_MODULE}/get/one/config?configId=${configId}`).then(res => {
+  //   return res;});
+  return axios.get(`${MOCK}/ml/configs/:configId`).then(res => {
     return res;});
-
 }
 
 /**
@@ -87,16 +85,15 @@ export const changeConfig = payload => {
 
 
 /**
- * 删除模型 GET /delete/{configId}
+ * 删除配置 GET /delete/{configId}
  * @param {*} payload
  * @returns
  */
-export const deleteModelByModelId = configId => {
-  //console.log("deletepara",payload);
-  //const { configId } = payload;
-  return axios.get(`${CONFIG_MODULE}/delete?configId=${configId}`).then(res => {
-
-    return res;
+export const deleteConfig = configId => {
+  // return axios.get(`${CONFIG_MODULE}/delete?configId=${configId}`).then(res => {
+  //   return res;
+    return axios.delete(`${MOCK}/ml/configs/${configId}`).then(res => {
+      return res;
   });
 }
 

+ 22 - 122
src/api/file.js

@@ -2,6 +2,7 @@
 import {FILE_MODULE} from "./_prefix";
 import request from '../utils/request';
 import {axios} from '../utils/request'
+import {MOCK} from "./_prefix";
 
 /**
  *  添加文件 POST /upload
@@ -9,7 +10,7 @@ import {axios} from '../utils/request'
  * @returns
  */
 export const UploadFile = payload => {
-    return axios.post(`${FILE_MODULE}/upload`,
+    return axios.post(`${FILE_MODULE}/file`,
         payload
         ,{
         headers:{
@@ -55,13 +56,17 @@ export const UploadFile = payload => {
  * @returns
  */
 export const GetAllFile = userId => {
-    return axios.get(`${FILE_MODULE}/get/all`, {
-        params:{
-            userId: userId
-        }
-    }).then( res => {
-        return res;
-    });
+    // return axios.get(`${FILE_MODULE}`, {
+    //     params:{
+    //         userId: userId
+    //     }
+    // }).then( res => {
+    //     return res;
+    // });
+    return axios.get(`${MOCK}/files:userId=1`, {
+        }).then( res => {
+            return res;
+        });
 }
 
 // 直接返回数据
@@ -82,130 +87,25 @@ export const GetAllFile = userId => {
  * @returns
  */
 export const getFileDetail = fileId => {
-    return axios.get(`${FILE_MODULE}/get/detail`, {
-        params: {
-            fileId: fileId
-        }
-    }).then( res => {
+    // return axios.get(`${FILE_MODULE}/${fileId}`).then( res => {
+    //     return res;
+    // })
+    return axios.get(`${MOCK}/files/:fileInfoId`).then( res => {
         return res;
     })
 }
 
-//直接返回数据:
-//     return Promise.resolve({
-//         "timestamp": 1649398931190,
-//         "status": 200,
-//         "data": {
-//             "fileName": "event_data",
-//             "createTime": "2022-03-29T09:25:43.154+00:00",
-//             "fileStrucInfo": {
-//                 "totalRows": "754"
-//             },
-//             "headerInfos": [
-//                 {
-//                     "id": 103,
-//                     "fileInfoId": 1,
-//                     "fieldName": "frame_index",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 1,
-//                     "fieldType": "int",
-//                     "valueInfo": "[\"79\",\"89992\"]"
-//                 },
-//                 {
-//                     "id": 104,
-//                     "fileInfoId": 1,
-//                     "fieldName": "time",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 1,
-//                     "fieldType": "double",
-//                     "valueInfo": "[\"3.16\",\"3599.68\"]"
-//                 },
-//                 {
-//                     "id": 105,
-//                     "fileInfoId": 1,
-//                     "fieldName": "road",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 0,
-//                     "fieldType": "string",
-//                     "valueInfo": "[\"R\",\"L\"]"
-//                 },
-//                 {
-//                     "id": 106,
-//                     "fileInfoId": 1,
-//                     "fieldName": "lane",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 0,
-//                     "fieldType": "string",
-//                     "valueInfo": "[\"r\",\"m\",\"l\"]"
-//                 },
-//                 {
-//                     "id": 107,
-//                     "fileInfoId": 1,
-//                     "fieldName": "vehicle_class",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 0,
-//                     "fieldType": "string",
-//                     "valueInfo": "[\"car\",\"truck\",\"bus\"]"
-//                 },
-//                 {
-//                     "id": 108,
-//                     "fileInfoId": 1,
-//                     "fieldName": "vehicle_id",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 1,
-//                     "fieldType": "int",
-//                     "valueInfo": "[\"20\",\"46862\"]"
-//                 },
-//                 {
-//                     "id": 109,
-//                     "fileInfoId": 1,
-//                     "fieldName": "event_type",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 0,
-//                     "fieldType": "string",
-//                     "valueInfo": "[\"cruise\",\"overdrive\"]"
-//                 },
-//                 {
-//                     "id": 110,
-//                     "fileInfoId": 1,
-//                     "fieldName": "duration",
-//                     "aliasName": "",
-//                     "fieldDes": "",
-//                     "nullsRatio": 0.0,
-//                     "conOrDis": 0,
-//                     "fieldType": "string",
-//                     "valueInfo": "[\"48,60\",\"24,36\",\"36,48\",\"12,24\",\"0,12\"]"
-//                 }
-//             ]
-//         }
-//     })
-// }
 /**
  *  删除文件 GET /delete
  * @param {*} fileId
  * @returns
  */
 export const deleteFile = fileId => {
-    return axios.get(`${FILE_MODULE}/delete`, {
-        params:{
-            fileId: fileId
-        }
-    }).then( res => {
-            return res;
+    // return axios.delete(`${FILE_MODULE}/${fileId}`).then( res => {
+    //         return res;
+    // });
+    return axios.delete(`${MOCK}/files/:fileInfoId`).then( res => {
+        return res;
     });
 }
 

+ 38 - 10
src/api/model.js

@@ -1,5 +1,6 @@
 import {axios} from '../utils/request'
 import {MODEL_MODULE} from "./_prefix";
+import {MOCK} from "./_prefix";
 
 /**
  * 新建模型 POST /model/add
@@ -7,15 +8,15 @@ import {MODEL_MODULE} from "./_prefix";
  * @returns
  */
 export const addModel = async payload => {
-    const {fileInfoId, configId, modelName, modelTypeId, argument} = payload;
-    const res = await axios.post(`${MODEL_MODULE}/add`, {
-        fileInfoId,
+    const {configId, userId, modelName, machineLearningAlgorithm, trainParams} = payload;
+    const res = await axios.post(`${MOCK}/ml/models`, {
         configId,
+        userId,
         modelName,
-        modelTypeId,
-        arguments:argument
+        machineLearningAlgorithm,
+        trainParams
     })
-    return res//response
+    return res
 }
 /**
  * 新建DL模型 POST /model/add/dl
@@ -49,9 +50,34 @@ export const deleteModel=async modelId=>{
      */
     export const getAllModel = async payload => {
         const{ userId } = payload;
-        const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
-        return res//response
+        // const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
+        const res = await axios.get(`${MOCK}/ml/models:userId=1`)
+        return res
     }
+/**
+ * 获取一个配置下的所有模型 GET
+ * @param userId
+ * @returns
+ */
+export const getModelByConfigId = async payload => {
+    const{ config } = payload;
+    // const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
+    const res = await axios.get(`${MOCK}/ml/models/configs/:configId`)
+    return res
+}
+
+/**
+ * 获取一个模型 GET
+ * @param userId
+ * @returns
+ */
+export const getOneModel = async payload => {
+    const{ modelId } = payload;
+    // const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
+    const res = await axios.get(`${MOCK}/ml/models/:modelId`)
+    return res
+}
+
     /**
      * 获取深度学习模型 GET /model/get/all/dl
      * @param userId
@@ -97,7 +123,8 @@ export const deleteModel=async modelId=>{
      */
     export const train = async payload=>{
         const { modelId } = payload;
-        const res=await axios.get(`${MODEL_MODULE}/train`,{params:{modelId:modelId}});
+        // const res=await axios.get(`${MODEL_MODULE}/train`,{params:{modelId:modelId}});
+        const res=await axios.get(`${MOCK}/ml/models/:modelId/train`);
         return res;
     }
     /**
@@ -108,7 +135,8 @@ export const deleteModel=async modelId=>{
      */
     export const use = async data=>{
         const {modelId,fileId}=data;
-        const res=await axios.get(`${MODEL_MODULE}/use?fileId=${fileId}&modelId=${modelId}`);
+        // const res=await axios.get(`${MODEL_MODULE}/use?fileId=${fileId}&modelId=${modelId}`);
+        const res=await axios.post(`${MOCK}/ml/predictions/predict:modelId=1&fileInfoId=2`);
         return res;
     }
 

+ 61 - 0
src/api/pipeline.js

@@ -0,0 +1,61 @@
+import {axios} from '../utils/request'
+import {PIPELINE_MODULE} from "./_prefix";
+import {MOCK} from "./_prefix";
+
+/**
+ * 新建流水线 POST
+ * @param {*} payload
+ * @returns
+ */
+export const addPipeline = async payload => {
+    const {userId, pipelineName, features, label, machineLearningAlgorithm, trainParams} = payload;
+    const res = await axios.post(`${MOCK}/ml/pipelines`, {
+        userId,
+        pipelineName,
+        features,
+        label,
+        machineLearningAlgorithm,
+        trainParams,
+    })
+    return res
+}
+
+/**
+ * 获取所有流水线
+ * @param {*} payload
+ * @returns
+ */
+export const getAllPipeline = payload => {
+    const {userId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
+    //   return res;
+    return axios.get(`${MOCK}/ml/pipelines/all/:userId`).then(res => {
+        return res;
+    });
+}
+/**
+ * 使用流水线
+ * @param {*} payload
+ * @returns
+ */
+export const usePipeline = payload => {
+    const {pipelineId, fileId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/get/all?userId=${uid}`).then(res => {
+    //   return res;
+    return axios.post(`${MOCK}/ml/pipelines/:pipelineId`).then(res => {
+        return res;
+    });
+}
+
+/**
+ * 删除流水线 GET
+ * @param {*} payload
+ * @returns
+ */
+export const deletePipeline = payload => {
+    const {pipelineId} = payload;
+    // return axios.get(`${CONFIG_MODULE}/delete?configId=${configId}`).then(res => {
+    return axios.delete(`${MOCK}/ml/pipelines/:pipelineId`).then(res => {
+        return res;
+    });
+}

+ 27 - 0
src/api/prediction.js

@@ -0,0 +1,27 @@
+import {axios} from '../utils/request'
+import {MODEL_MODULE, PREDICTION_MODULE} from "./_prefix";
+import {MOCK} from "./_prefix";
+
+/**
+ * 获取某一个模型下的预测结果 GET
+ * @param userId
+ * @returns
+ */
+export const getPrediction = async payload => {
+    const{ modelId } = payload;
+    // const res = await axios.get(`${MODEL_MODULE}/get/all`, {params: {userId: userId}})
+    const res = await axios.get(`${MOCK}/ml/predictions/models/:modelId`)
+    return res
+}
+
+/**
+ * 获取预测结果详情 GET
+ * @param modelId
+ * @returns
+ */
+export const getPredictionDetail = async payload => {
+    const{ predictionId } = payload;
+    // const res = await axios.get(`${MODEL_MODULE}/get/all/dl`, {params: {modelId: modelId}})
+    const res = await axios.get(`${MOCK}/ml/predictions/:predictionId`)
+    return res
+}

+ 8 - 0
src/api/utils.js

@@ -0,0 +1,8 @@
+import {axios} from '../utils/request'
+import {API_MODULE} from "./_prefix";
+import {MOCK} from "./_prefix";
+
+export const getEnum = () => {
+    const res = axios.get(`${API_MODULE}/hello/enum`)
+    return res
+}

+ 41 - 15
src/components/Sidebar.vue

@@ -72,11 +72,21 @@ export default {
             title: "我的文件",
           },
 
-          {
-              icon: "el-icon-lx-cascades",
-              index: "/config",
-              title: "模型配置",
-          },
+            {
+                icon: "el-icon-lx-cascades",
+                title: "配置",
+                index: 1,
+                subs: [
+                    {
+                        index: "/config",
+                        title: "模型配置"
+                    },
+                    {
+                        index: "/analysisConfig",
+                        title: "数据统计配置",
+                    },
+                ]
+            },
 
           // {
           //   icon: "el-icon-star-off",
@@ -96,11 +106,11 @@ export default {
           //   title:"深度学习模型",
           // },
 
-          {
-            icon: "el-icon-data-board",
-            index: "/charts",
-            title: "预测结果",
-          },
+          // {
+          //   icon: "el-icon-data-board",
+          //   index: "/charts",
+          //   title: "预测结果",
+          // },
 
           {
             icon: "el-icon-pie-chart",
@@ -108,11 +118,27 @@ export default {
             title: "数据分析",
           },
 
-          {
-            icon: "el-icon-office-building",
-            index: "/newSquare",
-            title: "数据广场",
-          },
+            {
+                icon: "el-icon-odometer",
+                title: "流水线",
+                index: 2,
+                subs: [
+                    {
+                        index: "/mlPipeline",
+                        title: "机器学习流水线"
+                    },
+                    {
+                        index: "/analysisPipeline",
+                        title: "数据分析流水线",
+                    },
+                ]
+            },
+
+            {
+                icon: "el-icon-office-building",
+                index: "/newSquare",
+                title: "数据广场",
+            },
 
           {
               icon: "el-icon-user",

+ 58 - 19
src/router/index.js

@@ -31,7 +31,7 @@ const routes = [
                 meta: {
                     title: '机器学习模型'
                 },
-                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/ModelList.vue")
+                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/ml/ModelList.vue")
             },
             // {
             //     path: "/dlModelList",
@@ -42,15 +42,6 @@ const routes = [
             //     component: () => import ( /* webpackChunkName: "dashboard" */ "../views/DLModelList.vue")
             // },
 
-            {
-                path: "/modelDetail",
-                name: "modelDetail",
-                meta: {
-                    title: '模型训练'
-                },
-                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/ModelDetail.vue")
-            },
-
             {
                 path: "/fileDetail",
                 name: "fileDetail",
@@ -72,15 +63,39 @@ const routes = [
                 meta: {
                     title: '模型配置'
                 },
-                component: () => import ( /* webpackChunkName: "table" */ "../views/ConfigList.vue")
+                component: () => import ( /* webpackChunkName: "table" */ "../views/ml/ConfigList.vue")
             },
             {
-                path: "/charts",
-                name: "basecharts",
+                path: "/predictionList",
+                name: "predictionList",
                 meta: {
-                    title: '训练结果'
+                    title: '预测结果列表'
                 },
-                component: () => import ( /* webpackChunkName: "charts" */ "../views/BaseCharts.vue")
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/ml/PredictionList.vue")
+            },
+            {
+                path: "/predictionDetail",
+                name: "predictionDetail",
+                meta: {
+                    title: '预测结果详情'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/ml/PredictionDetail.vue")
+            },
+            {
+                path: "/analysisConfig",
+                name: "analysisConfig",
+                meta: {
+                    title: '数据统计配置'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/analysis/AnalysisConfig.vue")
+            },
+            {
+                path: "/analysisConfigDetail",
+                name: "analysisConfigDetail",
+                meta: {
+                    title: '数据统计配置详情'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/analysis/AnalysisConfigDetail.vue")
             },
             {
                 path: "/analysis",
@@ -88,7 +103,31 @@ const routes = [
                 meta: {
                     title: '数据分析'
                 },
-                component: () => import ( /* webpackChunkName: "charts" */ "../views/Analysis.vue")
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/analysis/Analysis.vue")
+            },
+            {
+                path: "/analysisHistory",
+                name: "analysisHistory",
+                meta: {
+                    title: '数据分析结果'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/analysis/AnalysisHistory.vue")
+            },
+            {
+                path: "/analysisPipeline",
+                name: "analysisPipeline",
+                meta: {
+                    title: '数据分析流水线'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/analysis/AnalysisPipeline.vue")
+            },
+            {
+                path: "/mlPipeline",
+                name: "mlPipeline",
+                meta: {
+                    title: '机器学习流水线'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/ml/MLPipeline.vue")
             },
             {
                 path: "/form",
@@ -152,7 +191,7 @@ const routes = [
                 meta:{
                     title:'编辑配置'
                 },
-                component:()=>import(/*webpackChunkName:"configform" */ '../views/ConfigForm.vue')
+                component:()=>import(/*webpackChunkName:"configform" */ '../views/ml/ConfigForm.vue')
             },
             {
                 path:'/configformp/:id',
@@ -160,7 +199,7 @@ const routes = [
                 meta:{
                     title:'更改配置'
                 },
-                component:()=>import(/*webpackChunkName:"configform" */ '../views/changeConfig.vue')
+                component:()=>import(/*webpackChunkName:"configform" */ '../views/ml/changeConfig.vue')
             },
             {
                 path:'/ConfigDetail',
@@ -168,7 +207,7 @@ const routes = [
                 meta:{
                     title:'配置详情'
                 },
-                component:()=>import(/*webpackChunkName:"configform" */ '../views/ConfigDetail.vue')
+                component:()=>import(/*webpackChunkName:"configform" */ '../views/ml/ConfigDetail.vue')
             },
             //无用组件or路由
             // {

+ 0 - 171
src/views/BaseCharts.vue

@@ -1,171 +0,0 @@
-<template>
-    <div>
-              <!-- 历史记录,面包屑 -->
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item>
-                    <i class="el-icon-lx-cascades"></i> 基础表格
-                </el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-
-    <div class="container">
-
-    <!-- 详情页 -->
-
-
-    <div class="mytTable">
-         <el-table :data="tableData" border style="width: 100%">
-<!--         <el-table-column prop="event_type" label="目标字段" width="180" />-->
-           <el-table-column label="预测结果">
-             <template #default="scope">{{ scope.row }}</template>
-           </el-table-column>
-        </el-table>
-    </div>
-            <div class="pagination">
-                <el-pagination background layout="total, prev, pager, next" :current-page="query.pageIndex"
-                    :page-size="query.pageSize" :total="pageTotal" @current-change="handlePageChange"></el-pagination>
-            </div>
-    </div>
-
-    </div>
-</template>
-
-<script>
-import { ref, reactive } from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
-import { fetchData,fetchModuleDetail } from "../api";
-import {useRoute} from "vue-router";
-import {use}from "../api/model"
-
-export default {
-    
-    name:"basecharts",
-    setup() {
-        const query = reactive({
-            address: "",
-            name: "",
-            pageIndex: 1,
-            pageSize: 10,
-        });
-        const tableData = ref([]);
-        const moduleData = ref([]);
-        const pageTotal = ref(0);
-        let userId=ref(1);
-        const  dialogTableVisible = ref(false)
-        // 获取表格数据
-        //  插入获取数据的api,可以引入第三方文件
-        const getData = () => {
-          const route=useRoute()
-          const fileId=route.query.fileId
-          const modelId=route.query.modelId
-          console.log(fileId)
-          var data={
-            modelId:modelId,
-            fileId:fileId
-          }
-          ElMessageBox({
-            title: "正在拼命预测!",
-            beforeClose: (action, instance, done) => {
-              instance.confirmButtonLoading = true
-              instance.confirmButtonText = '预测中...'
-              use(data).then(res=>{
-                instance.confirmButtonLoading = false
-                if (res.status === 200)
-                {
-                  ElMessageBox({
-                    title: '模型使用',
-                    message: `模型预测成功`,
-                    confirmButtonText: '确定',
-                  })
-                  done()
-                }
-                else
-                {
-                  console.log(res);
-                  ElMessage.error("预测失败!失败码:"+res.status);
-                  done()
-                }
-                console.log(res.data);
-                tableData.value=res.data
-              }).catch(err=>{
-                console.log(err)
-              });
-              //超时自动关闭
-              setTimeout(() => {
-                ElMessageBox.close();
-                ElMessageBox('timeout');
-              }, 30000);
-
-              //console.log("getData被调用了")
-            }
-          })
-        };
-
-        // 相当于beforeCreated生命周期中调用了getData()函数
-        getData();
-
-       
-        // 分页导航
-        const handlePageChange = (val) => {
-            query.pageIndex = val;
-            getData();
-        };
-
-        const  handleDetail=({configId})=>{
-            
-            fetchModuleDetail(configId).
-            then((res)=>{
-                moduleData.value=res.data.list
-                dialogTableVisible.value=true
-                
-            })
-            // console.log(configId)
-        }
-
-        
-
-        return {
-            query,
-            tableData,
-            pageTotal,
-            userId,
-            handlePageChange,
-            handleDetail,
-            dialogTableVisible,
-            moduleData,
-        };
-    },
-};
-</script>
-
-<style scoped>
-.handle-box {
-    margin-bottom: 20px;
-}
-
-.handle-select {
-    width: 120px;
-}
-
-.handle-input {
-    width: 300px;
-    display: inline-block;
-}
-.table {
-    width: 100%;
-    font-size: 14px;
-}
-.red {
-    color: #ff0000;
-}
-.mr10 {
-    margin-right: 10px;
-}
-.table-td-thumb {
-    display: block;
-    margin: auto;
-    width: 40px;
-    height: 40px;
-}
-</style>

+ 0 - 373
src/views/ModelDetail.vue

@@ -1,373 +0,0 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item>
-                    <i class="el-icon-lx-cascades"></i> 模型训练使用
-                </el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-
-        <div class="container">
-            <div class="handle-box">
-                <el-select v-model="query.file" placeholder="模型信息" class="handle-select mr10">
-                    <el-option key="0" label="所有" value="0"></el-option>
-                    <el-option key="1" label="逻辑回归" value="1"></el-option>
-                    <el-option key="2" label="决策树" value="2"></el-option>
-                    <el-option key="3" label="随机森林" value="3"></el-option>
-                    <el-option key="4" label="梯度提升决策树" value="4"></el-option>
-                  <el-option key="5" label="多层感知器分类器" value="5"></el-option>
-                  <el-option key="6" label="朴素贝叶斯" value="6"></el-option>
-                  <el-option key="7" label="随机森林回归" value="7"></el-option>
-                  <el-option key="8" label="K-均值" value="8"></el-option>
-                </el-select>
-                <!-- <el-input v-model="query.name" placeholder="模型Id" class="handle-input mr10"></el-input>
-                <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button> -->
-            </div>
-            <el-table
-                :data="tableData" border class="table" ref="multipleTable"
-                header-cell-class-name="table-header"
-                @selection-change="handleSelectionChange"
-                @row-dblclick="getConfigDetail"
-            >
-                <el-table-column width="50" type="selection"></el-table-column>
-                <el-table-column prop="confName" label="模型名">
-                  <template #default="scope">{{ scope.row.modelName }}</template>
-                </el-table-column>
-                <el-table-column label="模型信息">
-                    <template #default="scope">{{ scope.row.modelTypeName }}</template>
-                </el-table-column>
-<!--              <el-table-column prop="fileName" label="待测试文件">-->
-<!--                    <template #default="scope">-->
-<!--                      <router-link :to="{path:'/ConfigDetail',query:{configId:scope.row.configId}}">-->
-<!--                        <el-button type="text" icon="el-icon-upload" >选择文件-->
-<!--                        </el-button>-->
-<!--                      </router-link>-->
-<!--                    </template>-->
-<!--              </el-table-column>-->
-
-                <el-table-column label="模型训练" width="180" align="center">
-                    <template #default="scope">
-                        <el-button type="text" icon="el-icon-video-play" class="green"
-                            @click="handleTrain(scope.row.modelId)">模型训练</el-button>
-                    </template>
-                </el-table-column>
-                <!-- 如果已经训练了,那么就告诉她您已经训练过该模型,但还是感觉应该放在模型配置里面,配置就可以让他进行训练! -->
-
-
-<!--                <el-table-column label="操作" width="180" align="center">-->
-<!--                    <template #default="scope">-->
-<!--                        <el-button type="text" icon="el-icon-mouse" class="blue"-->
-<!--                            @click="handleDelete(scope.$index, scope.row)"-->
-<!--                        >模型使用</el-button>-->
-<!--                    </template>-->
-<!--                </el-table-column>-->
-            </el-table>
-            <div class="pagination">
-                <el-pagination background layout="total, prev, pager, next" :current-page="query.pageIndex"
-                    :page-size="query.pageSize" :total="pageTotal" @current-change="handlePageChange"></el-pagination>
-            </div>
-        </div>
-
-        <!-- 编辑弹出框 -->
-        <el-dialog title="编辑" v-model="editVisible" min-width="30%">
-            <el-form label-width="150px">
-                <el-form-item label="配置名称" required>
-                    <el-input v-model="form.name"></el-input>
-                </el-form-item>
-                <el-form-item label="训练文件" required>
-                    <el-input v-model="form.file"></el-input>
-                </el-form-item>
-                <el-form-item label="选择模型类别" required>
-                  <el-radio-group v-model="modelSty.resource">
-                  <el-radio label="分类"></el-radio>
-                  <el-radio label="聚类"></el-radio>
-                  <el-radio label="回归"></el-radio>
-                  <el-radio label="自动化分类器"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-
-                <el-form-item label="特征选择方式" required>
-<!--                    <el-checkbox v-for="f in featureList" :key="f" :label="f"></el-checkbox>-->
-                  <el-radio-group v-model="featureSty.resource">
-                    <el-radio label="手动选择"></el-radio>
-                    <el-radio label="自动选择"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-
-                <el-form-item label="选择特征字段" required>
-                  <el-alert type="info" description="您下列选择的第一个字段为分类模型的目标字段"
-                            show-icon style="height: 40px;"></el-alert>
-                </el-form-item>
-            </el-form>
-            <template #footer>
-                <span class="dialog-footer">
-                    <el-button @click="editVisible = false">取 消</el-button>
-                    <el-button type="primary" @click="saveEdit">确 定</el-button>
-                </span>
-            </template>
-        </el-dialog>
-    </div>
-</template>
-
-<script>
-import { ref, reactive } from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
-import { fetchData } from "../api";
-import {addConfig, deleteModelByModelId, getAllConfig} from "../api/config";
-import {getAllModel, train} from "../api/model";
-
-export default {
-    name: "config",
-    //训练特征
-
-  setup() {
-    const featureSty = reactive({
-
-      resource: "小天才",
-
-    });
-    const modelSty = reactive({
-
-      resource: "小天才",
-
-    });
-        const query = reactive({
-            file: "",
-            name: "",
-            pageIndex: 1,
-            pageSize: 10,
-        });
-
-        //new TODO
-        const typeList=["所有","分类","聚类","回归","自动分类器"];
-        const featureList=["所有","手动选择","自动选择"];
-        const multipleSelection =ref([]);
-
-        // const tableData = ref([]);
-    const tableData = ref([
-      ]);
-
-        const pageTotal = ref(0);
-        // 获取表格数据
-
-    const getpage=()=> {
-      // console.log("getpage");
-      //getAllConfig({
-        //uid: 1
-      getAllModel({
-        userId: 1
-      }).then(res => {
-        tableData.value = res.data;
-
-      })
-    };
-     getpage();
-
-
-
-    // 查询操作
-        const handleSearch = () => {
-            query.pageIndex = 1;
-            getData();
-        };
-        // 分页导航
-        const handlePageChange = (val) => {
-            query.pageIndex = val;
-            getData();
-        };
-
-        const handleTrain =(modelId) => {
-          ElMessageBox( {
-            title: "确定要训练吗?",
-            showCancelButton: true,
-            confirmButtonText: '确定',
-            cancelButtonText: '取消',
-            beforeClose: (action, instance, done) => {
-              if (action === 'confirm') {
-                instance.confirmButtonLoading = true
-                instance.confirmButtonText = '训练中...'
-                let idx=modelId;
-                console.log("modelId: "+idx);
-                train({
-                  modelId: idx
-                }).then(res=>{
-                  instance.confirmButtonLoading = false
-                  console.log("train_res: ",res);
-                  if (res.status === 200 & res.data === true)
-                  {
-                    ElMessageBox({
-                      title: '模型训练',
-                      message: `模型训练成功`,
-                      confirmButtonText: '确定',
-                    })
-                    done()
-                  }
-                  else
-                  {
-                    console.log(res);
-                    ElMessage.error("训练失败!失败码:"+res.status);
-                    done()
-                  }
-                }).catch(err=>{
-                  console.log(err)
-                });
-              } else {
-                done()
-              }
-            },
-          })
-        };
-
-        // 删除操作(单个)
-        const handleDelete = (index) => {
-          ElMessage.success("正在训练中...")
-            // 二次确认删除
-            // ElMessageBox.confirm("确定要删除吗?", "提示", {
-            //     type: "warning",
-            // })
-            //     .then(() => {
-            //         // ElMessage.success("删除成功");
-            //         // tableData.value.splice(index, 1);
-            //       console.log("删除");
-            //       let idx=tableData._rawValue[index].id;
-            //       console.log(idx);
-            //       deleteModelByModelId({
-            //         configId:idx
-            //       }).then(res=>{
-            //         console.log("deleteres",res);
-            //       });
-            //
-            //     })
-            //     .catch(() => {});
-        };
-
-        //TODO
-        const handleSelectionChange = (val) => {
-          multipleSelection.value = val
-        };
-
-        //TODO 批量删除
-        const handleBatchDelete =()=>{
-          // 二次确认
-          if(multipleSelection.value.length<=0){
-            ElMessageBox.alert("请先勾选要删除的模型", '提示', {
-              confirmButtonText: '好的'
-            })
-          }
-          else{
-            ElMessageBox.confirm("确定要删除这些模型吗?", "提示", {
-              type: "warning",
-            })
-                .then(() => {
-                  let idList=[];
-                  for(let i=0;i<multipleSelection.value.length;i++){
-                    let id=multipleSelection.value[i]['id'];
-                    idList.push(id);
-                  }
-                  idList.sort();
-                  console.log(idList);
-                  for (let i = idList[idList.length-1]; i >=0 ; i--) {
-                    tableData.value.splice(i,1);
-                  }
-                  ElMessage.success("删除成功");
-                })
-                .catch(() => {});
-          }
-        };
-
-        const getConfigDetail=(row,column,event)=>{
-          var configId=row.configId;
-          //todo:双击跳转到config界面
-          console.log("跳转到config详情界面")
-        }
-        // 表格编辑时弹窗和保存
-        const editVisible = ref(false);
-        let form = reactive({
-            name: "",
-            file: "",
-          // classification:"",
-          // feature:"",
-        });
-        let idx = -1;
-        const handleEdit = (index, row) => {
-            idx = index;
-            Object.keys(form).forEach((item) => {
-                form[item] = row[item];
-            });
-            editVisible.value = true;
-        };
-
-        const saveEdit = () => {
-            editVisible.value = false;
-            // ElMessage.success(`修改第 ${idx + 1} 行成功`);
-            // Object.keys(form).forEach((item) => {
-            //     tableData.value[idx][item] = form[item];
-            // });
-          // console.log("here",featureSty.resource);
-          // const { , ,confName , fieldIds} = payload;
-
-          addConfig({
-            fileInfoId:form.file,
-            userId:0,
-            modelTypeId:modelSty.resource,
-            confName:form.name,
-            fieldIds:featureSty.resource
-            }).then(res=>{
-              console.log(res);
-            });
-        };
-
-        return {
-            query,
-            tableData,
-            pageTotal,
-            editVisible,
-            form,
-            handleSearch,
-            handlePageChange,
-          handleTrain,
-            handleDelete,
-          handleSelectionChange,
-            handleBatchDelete,
-            handleEdit,
-            saveEdit,
-          getConfigDetail,
-            featureSty,
-          modelSty,
-          getpage,
-        };
-    },
-};
-</script>
-
-<style scoped>
-.handle-box {
-    margin-bottom: 20px;
-}
-
-.handle-select {
-    width: 120px;
-}
-
-.handle-input {
-    width: 300px;
-    display: inline-block;
-}
-.table {
-    width: 100%;
-    font-size: 14px;
-}
-.red {
-    color: #ff0000;
-}
-.mr10 {
-    margin-right: 10px;
-}
-.table-td-thumb {
-    display: block;
-    margin: auto;
-    width: 40px;
-    height: 40px;
-}
-</style>

+ 335 - 0
src/views/analysis/Analysis.vue

@@ -0,0 +1,335 @@
+<template>
+<div>
+  <div class="crumbs">
+    <el-breadcrumb separator="/">
+      <el-breadcrumb-item>
+        <i class="el-icon-lx-cascades"></i> 数据统计分析列表
+      </el-breadcrumb-item>
+    </el-breadcrumb>
+  </div>
+
+  <div class="container">
+    <el-table
+        :data="tableData" border class="table" ref="multipleTable"
+        header-cell-class-name="table-header"
+    >
+      <el-table-column prop="configName" label="配置名"></el-table-column>
+      <el-table-column prop="fileId" label="文件id"></el-table-column>
+      <el-table-column label="查看历史分析">
+        <template #default="scope">
+          <el-button type="text" icon="el-icon-circle-check" class="yellow"
+                     @click="handleAnalysisResult(scope.row.id)">查看分析结果</el-button>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+          <el-button type="text" icon="el-icon-mouse"
+                     @click="startAnalysis(scope.$index)">分析
+          </el-button>
+          <el-button type="text" icon="el-icon-stopwatch"
+                     @click="handlePipeline(scope.row.id)">新建流水线
+          </el-button>
+          <el-button type="text" icon="el-icon-delete" class="red"
+                     @click="handleDelete(scope.row.id)">删除</el-button>
+        </template>
+      </el-table-column>
+
+    </el-table>
+
+    <el-dialog title="新建统计分析配置" v-model="analysisVisible" width="50%">
+      <el-form label-width="150px">
+        <el-form-item label="配置拥有字段数:" >
+          {{selectConfigLen}}
+        </el-form-item>
+        <el-form-item label="分析方法" required>
+          <el-select v-model="analysisType" placeholder="分析方法" class="handle-select mr10" @change="changeMethodMsg">
+            <el-option key="1" label="数据分布" value="DATA_DISTRIBUTION"></el-option>
+            <el-option key="2" label="散点图" value="SCATTER_PLOT"></el-option>
+            <el-option key="3" label="数据统计" value="STATISTICS"></el-option>
+            <el-option key="4" label="偏度-峰度" value="SKEWNESS_KURTOSIS"></el-option>
+            <el-option key="5" label="皮尔森相关系数" value="PEARSON"></el-option>
+          </el-select>
+        </el-form-item>
+          <el-alert type="info" show-icon style="height: 40px;">{{methodMsg}}</el-alert>
+        <el-form-item label="方法和字段要求" required>
+          <text>皮尔森相关系数需要两个int/double字段,散点图可以传入2-3个字段,其他的方法只需一个字段。除了数据分布可以传入string字段,其他方法请传入int/double字段</text>
+        </el-form-item>
+                  <el-form-item label="配置拥有字段" required>
+                    <el-table :data="selectConfigCol" border class="table"  header-cell-class-name="table-header" >
+                      <el-table-column label="字段名">
+                        <template #default="scope"><span>{{scope.row}}</span></template>
+                      </el-table-column>
+                    </el-table>
+                  </el-form-item>
+      </el-form>
+      <template #footer>
+                <span class="dialog-footer">
+                    <el-button @click="analysisVisible = false">取 消</el-button>
+                    <el-button type="primary" @click="analysis">确 定</el-button>
+                </span>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+        v-model="pipelineVisible"
+        title="新建流水线"
+        width="30%"
+        :before-close="handleClose"
+    >
+      <el-form width="300px">
+        <el-form-item label="流水线名称" required>
+          <el-input v-model="pplForm.pipelineName"></el-input>
+        </el-form-item>
+        <el-form-item label="分析方法" required>
+          <el-select v-model="pplForm.func" placeholder="分析方法" class="handle-select mr10">
+            <el-option key="1" label="数据分布" value="DATA_DISTRIBUTION"></el-option>
+            <el-option key="2" label="散点图" value="SCATTER_PLOT"></el-option>
+            <el-option key="3" label="数据统计" value="STATISTICS"></el-option>
+            <el-option key="4" label="偏度-峰度" value="SKEWNESS_KURTOSIS"></el-option>
+            <el-option key="5" label="皮尔森相关系数" value="PEARSON"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="选中列:" >
+          {{pplForm.chosenColumns}}
+        </el-form-item>
+      </el-form>
+      <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="pipelineVisible = false">取消</el-button>
+            <el-button type="primary" @click="pipeline()" style="margin-left: 10px"> 新建流水线 </el-button>
+          </span>
+      </template>
+    </el-dialog>
+
+  </div>
+
+</div>
+</template>
+
+<script>
+import {reactive, ref} from "vue";
+import {deleteAnalysisConfig, getAnalysisConfig, getOneAnalysisConfig} from "../../api/analysisConfig";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {visualize} from "../../api/analysis";
+import {useRouter} from "vue-router";
+import {addAnalysisPipeline} from "../../api/analysisPipeline";
+
+export default {
+  name: "Analysis",
+  setup(){
+    const tableData = ref([]);
+    const pageTotal = ref(0);
+    let analysisVisible = ref(false);
+    let selectConfigLen = ref();
+    let selectConfigId = ref();
+    let analysisType = ref();
+    let selectConfigCol = ref([]);
+    let methodMsg=ref("不同方法仅适用于特定特征字段");
+    const router = useRouter();
+
+    const changeMethodMsg=()=>{
+      let value = analysisType.value;
+      if(value==='DATA_DISTRIBUTION'){
+        methodMsg.value="数据分布请传入1个字段,类型不限"
+      }else if(value==='SCATTER_PLOT'){
+        methodMsg.value="散点图可以传入2个int/double字段"
+      }else if(value==='STATISTICS'){
+        methodMsg.value="请传入1个int/double字段"
+      }else if(value==='SKEWNESS_KURTOSIS'){
+        methodMsg.value="请传入1个int/double字段"
+      }else if(value==='PEARSON'){
+        methodMsg.value="请传入2个int/double字段"
+      }
+    }
+
+    let pipelineVisible = ref(false)
+    const pplForm = reactive({
+      func: '',
+      pipelineName: '',
+      chosenColumns: [],
+    })
+    const handlePipeline = (configId) => {
+      console.log(configId)
+      pipelineVisible.value = true
+      getOneAnalysisConfig({configId}).then(res =>{
+        pplForm.chosenColumns = res.result.chosenColumns
+      })
+    }
+    const pipeline = () => {
+      console.log(pplForm)
+      const payload = {
+        userId: 1,
+        pipelineName: pplForm.pipelineName,
+        analysisFunction: pplForm.func,
+        chosenColumns: pplForm.chosenColumns
+      }
+      if (payload.pipelineName === ""){
+        alert("请填写信息!")
+        return
+      }
+      addAnalysisPipeline(payload).then(res => {
+        if (res.msg === '成功'){
+          pipelineVisible.value = false
+          ElMessageBox({
+            title: '模型训练',
+            message: `加入流水线成功`,
+            confirmButtonText: '确定',
+          })
+        }
+        else{
+          ElMessage.error("预测失败!失败码:"+res.msg);
+        }
+      })
+    }
+
+    const getPage=()=> {
+      getAnalysisConfig({
+        uid: 1
+      }).then(res => {
+        tableData.value = res.result.configVOList;
+      })
+    };
+    getPage();
+
+    const handleDelete = (id) => {
+      ElMessageBox.confirm("确定要删除吗?", "提示", {
+        type: "warning",
+      })
+          .then(() => {
+            deleteAnalysisConfig({id}
+            ).then(res=>{
+              if(res.msg === '成功'){
+                ElMessage.success("删除成功!")
+                //location.reload();
+                getPage();
+              }
+            });
+          })
+          .catch(() => {});
+    };
+
+    const handleAnalysisResult = (configId) => {
+      router.push({
+        path:'analysisHistory',
+        query:{
+          configId: configId,
+        }
+      })
+    };
+
+    const startAnalysis = (index) => {
+      selectConfigId = tableData.value[index].id;
+      selectConfigLen.value = tableData.value[index].chosenColumns.length;
+      selectConfigCol.value = tableData.value[index].chosenColumns;
+      analysisVisible.value = true;
+    };
+
+    const analysis = () => {
+      const payload={
+        userId: 1,
+        configId: selectConfigId,
+        func: analysisType.value,
+      }
+      if (payload.func !== undefined){
+        analysisVisible.value = false;
+        ElMessageBox( {
+          title: "确定要分析吗?",
+          showCancelButton: true,
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true
+              instance.confirmButtonText = '分析中...'
+              visualize(payload).then(res => {
+                instance.confirmButtonLoading = false
+                if (res.msg === '成功'){
+                  ElMessageBox({
+                    title: '数据分析',
+                    message: `数据分析成功`,
+                    confirmButtonText: '确定',
+                  })
+                  getPage();
+                  done()
+                }
+                else{
+                  ElMessage.error("数据分析失败!失败码:"+res.code);
+                  done()
+                }
+              }).catch(err=>{
+                console.log(err)
+              });
+            } else {
+              done()
+            }
+          },
+        })
+      }
+      else {
+        alert("请选择数据分析方法!");
+        return;
+      }
+    }
+
+    return{
+      tableData,
+      pageTotal,
+      analysisVisible,
+      selectConfigLen,
+      analysisType,
+      selectConfigCol,
+      methodMsg,
+      selectConfigId,
+      router,
+      pplForm,
+      pipelineVisible,
+      getPage,
+      handleDelete,
+      handleAnalysisResult,
+      startAnalysis,
+      analysis,
+      changeMethodMsg,
+      handlePipeline,
+      pipeline,
+    };
+  }
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+}
+.green {
+  color: rgb(32, 220, 88)
+}
+.yellow{
+  color:yellowgreen;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 117 - 0
src/views/analysis/AnalysisConfig.vue

@@ -0,0 +1,117 @@
+<template>
+  <div>
+    <div class="crumbs">
+      <el-breadcrumb separator="/">
+        <el-breadcrumb-item>
+          <i class="el-icon-lx-cascades"></i> 数据统计配置列表
+        </el-breadcrumb-item>
+      </el-breadcrumb>
+    </div>
+
+    <div class="container">
+      <el-table
+          :data="tableData" border class="table" ref="multipleTable"
+          header-cell-class-name="table-header"
+      >
+        <el-table-column prop="configName" label="配置名"></el-table-column>
+        <el-table-column prop="fileId" label="文件id"></el-table-column>
+        <el-table-column prop="chosenColumns" label="选中字段"></el-table-column>
+        <el-table-column label="操作" width="180" align="center">
+          <template #default="scope">
+            <router-link :to="{path:'/analysisConfigDetail',query:{configId: scope.row.id}}">
+              <el-button type="text" icon="el-icon-view" >查看详情
+              </el-button>
+            </router-link>
+            <el-button type="text" icon="el-icon-delete" class="red"
+                       @click="handleDelete(scope.row.id)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+
+    </div>
+
+  </div>
+</template>
+
+<script>
+import {ref} from "vue";
+import {deleteAnalysisConfig, getAnalysisConfig} from "../../api/analysisConfig";
+import {ElMessage, ElMessageBox} from "element-plus";
+
+export default {
+  name: "AnalysisConfig",
+  setup(){
+    const tableData = ref([]);
+    const pageTotal = ref(0);
+
+    const getPage=()=> {
+      getAnalysisConfig({
+        uid: 1
+      }).then(res => {
+        console.log("res",res);
+        tableData.value = res.result.configVOList;
+      })
+    };
+    getPage();
+
+    const handleDelete = (id) => {
+      ElMessageBox.confirm("确定要删除吗?", "提示", {
+        type: "warning",
+      })
+          .then(() => {
+            deleteAnalysisConfig({id}
+            ).then(res=>{
+              if(res.msg === '成功'){
+                ElMessage.success("删除成功!")
+                //location.reload();
+                getPage();
+              }
+            });
+          })
+          .catch(() => {});
+    }
+
+
+    return{
+      tableData,
+      pageTotal,
+      getPage,
+      handleDelete,
+    };
+  }
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+  margin-left: 10px;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 233 - 0
src/views/analysis/AnalysisConfigDetail.vue

@@ -0,0 +1,233 @@
+<template>
+  <div>
+
+    <div class="crumbs">
+      <el-breadcrumb separator="/">
+        <el-breadcrumb-item>
+          <i class="el-icon-lx-cascades"></i> 数据统计配置详情
+        </el-breadcrumb-item>
+      </el-breadcrumb>
+    </div>
+
+    <div class="container">
+      <div class="form-box" >
+        <el-form ref="formRef" label-width="120px">
+          <el-form-item label="统计配置名:" >
+            {{config.configName}}
+          </el-form-item>
+          <el-form-item label="选中字段:" >
+              {{config.chosenColumns}}
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary"  @click="startAnalysis()">进行分析</el-button>
+          </el-form-item>
+        </el-form>
+
+      </div>
+    </div>
+
+    <el-dialog title="新建统计分析配置" v-model="analysisVisible" width="50%">
+      <el-form label-width="150px">
+        <el-form-item label="配置拥有字段数:" >
+          {{selectConfigLen}}
+        </el-form-item>
+        <el-form-item label="分析方法" required>
+          <el-select v-model="analysisType" placeholder="分析方法" class="handle-select mr10" @change="changeMethodMsg">
+            <el-option key="1" label="数据分布" value="DATA_DISTRIBUTION"></el-option>
+            <el-option key="2" label="散点图" value="SCATTER_PLOT"></el-option>
+            <el-option key="3" label="数据统计" value="STATISTICS"></el-option>
+            <el-option key="4" label="偏度-峰度" value="SKEWNESS_KURTOSIS"></el-option>
+            <el-option key="5" label="皮尔森相关系数" value="PEARSON"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-alert type="info" show-icon style="height: 40px;">{{methodMsg}}</el-alert>
+        <el-form-item label="方法和字段要求" required>
+          <text>皮尔森相关系数需要两个int/double字段,散点图可以传入2-3个字段,其他的方法只需一个字段。除了数据分布可以传入string字段,其他方法请传入int/double字段</text>
+        </el-form-item>
+        <el-form-item label="配置拥有字段" required>
+          <el-table :data="selectConfigCol" border class="table"  header-cell-class-name="table-header" >
+            <el-table-column label="字段名">
+              <template #default="scope"><span>{{scope.row}}</span></template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+                <span class="dialog-footer">
+                    <el-button @click="analysisVisible = false">取 消</el-button>
+                    <el-button type="primary" @click="analysis">确 定</el-button>
+                </span>
+      </template>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {reactive, ref} from "vue";
+import {useRoute} from "vue-router";
+import {getOneAnalysisConfig} from "../../api/analysisConfig";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {visualize} from "../../api/analysis";
+
+export default {
+  name: "AnalysisConfigDetail",
+  setup(){
+    const query = reactive({
+      address: "",
+      name: "",
+      pageIndex: 1,
+      pageSize: 10,
+    });
+    const fileId=ref(1);
+    const selectData = ref([]);
+    const pageTotal = ref(0);
+    const config=ref({});
+    const route=useRoute()
+    let analysisVisible = ref(false);
+    let analysisType = ref();
+    let selectConfigCol = ref([]);
+    let methodMsg=ref("不同方法仅适用于特定特征字段");
+    let selectConfigLen = ref();
+
+
+    let form = reactive({
+      modelTypeId:"",
+      configId:route.query.configId,
+      fileInfoId:"",
+      modelName:"",
+    });
+
+    const changeMethodMsg=()=>{
+      let value = analysisType.value;
+      if(value==='DATA_DISTRIBUTION'){
+        methodMsg.value="数据分布请传入1个字段,类型不限"
+      }else if(value==='SCATTER_PLOT'){
+        methodMsg.value="散点图可以传入2个int/double字段"
+      }else if(value==='STATISTICS'){
+        methodMsg.value="请传入1个int/double字段"
+      }else if(value==='SKEWNESS_KURTOSIS'){
+        methodMsg.value="请传入1个int/double字段"
+      }else if(value==='PEARSON'){
+        methodMsg.value="请传入2个int/double字段"
+      }
+    }
+
+    const getData = () => {
+      if(route.query.configId!=null) {
+        getOneAnalysisConfig({configId:route.query.configId}).then(res => {
+          config.value=res.result;
+          fileId.value=res.result.fileId;
+          form.fileInfoId=(String)(fileId.value)
+          selectConfigCol.value=res.result.chosenColumns
+          selectConfigLen.value=res.result.chosenColumns.length
+        })
+      }
+    };
+    getData();
+
+    const startAnalysis= () => {
+      analysisVisible.value = true;
+    }
+
+    const analysis = () => {
+      const payload={
+        userId: 1,
+        configId: form.configId,
+        func: analysisType.value,
+      }
+      if (payload.func !== undefined){
+        analysisVisible.value = false;
+        ElMessageBox( {
+          title: "确定要分析吗?",
+          showCancelButton: true,
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true
+              instance.confirmButtonText = '分析中...'
+              visualize(payload).then(res => {
+                instance.confirmButtonLoading = false
+                if (res.msg === '成功'){
+                  ElMessageBox({
+                    title: '数据分析',
+                    message: `数据分析成功`,
+                    confirmButtonText: '确定',
+                  })
+                  getData();
+                  done()
+                }
+                else{
+                  ElMessage.error("数据分析失败!失败码:"+res.code);
+                  done()
+                }
+              }).catch(err=>{
+                console.log(err)
+              });
+            } else {
+              done()
+            }
+          },
+        })
+      }
+      else {
+        alert("请选择数据分析方法!");
+        return;
+      }
+    }
+
+    return{
+      config,
+      query,
+      pageTotal,
+      form,
+      fileId,
+      selectData,
+      analysisVisible,
+      analysisType,
+      selectConfigCol,
+      methodMsg,
+      selectConfigLen,
+      analysis,
+      getData,
+      startAnalysis,
+      changeMethodMsg,
+    };
+  }
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+.handle-table{
+  margin-bottem: 40px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 313 - 0
src/views/analysis/AnalysisHistory.vue

@@ -0,0 +1,313 @@
+<template>
+<div>
+  <div class="crumbs">
+    <el-breadcrumb separator="/">
+      <el-breadcrumb-item>
+        <i class="el-icon-lx-cascades"></i> 分析结果
+      </el-breadcrumb-item>
+    </el-breadcrumb>
+  </div>
+
+  <div class="container">
+    <el-table
+        :data="tableData" border class="table" ref="multipleTable"
+        header-cell-class-name="table-header"
+    >
+      <el-table-column prop="analysisConfigId" label="配置id"></el-table-column>
+      <el-table-column label="分析方法">
+        <template #default="scope">{{ scope.row.analysisFunction }}</template>
+      </el-table-column>
+      <el-table-column label="结果">
+        <template #default="scope">
+          <div v-if="scope.row.analysisFunction === 'SCATTER_PLOT'">
+              请移步可视化👉
+          </div>
+          <div v-else>
+            {{ scope.row.analysisResult }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+            <el-button type="text" icon="el-icon-view"
+                       @click="handleVisualize(scope.$index)">可视化
+            </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+
+
+
+
+  <el-dialog v-model="visualizeVisible" title="可视化" @open="open(chart.analysisFunction)" width="700px">
+    <el-form :model="chart" width="400px">
+      <el-form-item label="分析函数: " >
+          {{chart.analysisFunction}}
+      </el-form-item>
+      <el-form-item label="分析结果: " v-if="chart.analysisFunction!=='SCATTER_PLOT'">
+        {{chart.analysisResult}}
+      </el-form-item>
+      <el-form-item label="可视化: " v-if="chart.analysisFunction === 'DATA_DISTRIBUTION'">
+        <div id="chartPie" style="width: 600px;height: 300px" ></div>
+      </el-form-item>
+      <el-form-item label="可视化: " v-if="chart.analysisFunction === 'SCATTER_PLOT'">
+        <div id="scatter" style="width: 600px;height: 300px" ></div>
+      </el-form-item>
+
+  </el-form>
+  </el-dialog>
+</div>
+</template>
+
+<script>
+import {useRoute} from "vue-router";
+import {inject, nextTick, reactive, ref} from "vue";
+import {getAnalysisByConfig} from "../../api/analysis";
+
+export default {
+  name: "AnalysisHistory",
+  setup(){
+    const tableData = ref([]);
+    const configId = ref();
+    let visualizeVisible = ref(false)
+    const chart = reactive({
+      analysisFunction: '',
+      analysisResult: {},
+      pieResult: {},
+    })
+    let scatterData = ref([])
+    let scatterKey = ref([])
+
+    const echarts = inject('echarts')
+    const echartsScatter = inject('echarts')
+
+    const getData = () => {
+      const route=useRoute()
+      if(route.query.configId!=null) {
+        getAnalysisByConfig(route.query.configId).then(res => {
+          tableData.value = res.result.visualizationVOList;
+          for (let i=0;i<tableData.value.length;i++){
+            tableData.value[i].analysisResult=JSON.stringify(tableData.value[i].analysisResult)
+          }
+          configId.value=route.query.configId
+        })
+      }
+    };
+    getData();
+
+    const handleVisualize = (index) => {
+      chart.analysisFunction = tableData.value[index].analysisFunction
+
+      if(chart.analysisFunction === 'SCATTER_PLOT'){
+        let tempArr= JSON.parse(tableData.value[index].analysisResult)
+        let tempObj = tempArr[0]
+        let out_arr = []
+        for(let key in tempObj){
+          out_arr.push(key)
+        }
+        scatterKey = out_arr
+        let tempScatterData = []
+        for (let i in tempArr){
+          let temp = []
+          for (let j in tempArr[i]){
+            temp.push(tempArr[i][j])
+          }
+          tempScatterData.push(temp)
+        }
+        scatterData = tempScatterData
+      }
+      else {
+        chart.analysisResult = tableData.value[index].analysisResult
+      }
+
+      visualizeVisible.value = true
+
+      if(chart.analysisFunction === 'DATA_DISTRIBUTION'){
+        let out_arr = []
+        let in_obj = JSON.parse(chart.analysisResult)
+        for(let key in in_obj){
+          let temp = {};			//创建临时对象
+          temp.name = key;		//存储对象的Key为name
+          temp.value = in_obj[key];	//存储value
+          out_arr.push(temp);
+        }
+        chart.pieResult = out_arr
+      }
+
+    }
+
+    const drawChart = () => {
+      let myChart = echarts.init(document.getElementById("chartPie"));
+      myChart.setOption({
+        //鼠标悬浮
+        tooltip: {
+          trigger: "item",
+          // formatter: "{a}<br/>{b}: <br/>{c}({d}%)",  其中 {a}指向name名称(访问来源)
+          formatter: "{b}: <br/>{c}({d}%)",
+        },
+        // legend: {
+        //   // data: ["直接访问", "邮件营销", "联盟广告", "视频广告", "搜索引擎"],
+        //   right: 300,
+        //   orient: "vertical",
+        //   // 下面注释的代码是控制分类放在哪个地方,需要体验的话,直接把上面的代码注释,把下面的代码解开注释即可
+        //     data: ["红", "黄", "绿"],
+        //     // left: "center",
+        //     // top: "bottom",
+        //     // orient: "horizontal"
+        // },
+        series: [
+          {
+            name: "???",
+            type: "pie",
+            //圆圈的粗细
+            radius: ["50%", "80%"],
+            //圆圈的位置
+            center: ["50%", "50%"],
+            data:  chart.pieResult,
+            animationDuration: 2000,
+            //控制是否显示指向文字的,默认为true
+            label: {
+              // show: false,
+              // position: "center",
+              //以下代码可以代表指向小文字的
+                show: true,
+                formatter: "{b} : {d}%",
+                textStyle: {
+                  color: "#333",
+                  fontSize: 14,
+                },
+            },
+          },
+        ],
+      })
+      window.onresize = function () {
+        myChart.resize()
+      }
+    }
+
+    const drawScatter = () => {
+      let myChart = echartsScatter.init(document.getElementById("scatter"));
+      myChart.setOption({
+        color: [
+          '#dd4444', '#fec42c', '#80F1BE'
+        ],
+        grid:{
+          right: '20%'
+        },
+        tooltip: {
+          //十字线
+          axisPointer: {
+            type: 'cross'
+          },
+          padding: 10,
+          backgroundColor: '#222',
+          borderColor: '#777',
+          borderWidth: 1,
+          //设置tooltip的显示内容
+          formatter: function (obj) {
+            let value = obj.value;
+            return '<div style="border-bottom: 1px solid rgba(255,255,255,.3); font-size: 18px;padding-bottom: 7px;margin-bottom: 7px">'
+                +  '具体数据:'
+                + '</div>'
+                + scatterKey[0] + ':' + value[0] + '<br>'
+                + scatterKey[1] + ':' + value[1] + '<br>'
+          }
+        },
+        xAxis: {
+          name: scatterKey[0],
+          splitLine: {
+            show: false
+          },
+        },
+        yAxis: {
+          name: scatterKey[1],
+          splitLine: {
+            show: false
+          }
+        },
+        series: [
+          {
+            type: 'scatter',
+            itemStyle: {
+              shadowBlur: 10,
+              shadowColor: 'rgba(120, 36, 50, 0.5)',
+              shadowOffsetY: 5,
+            },
+            data: scatterData,
+          }
+        ]
+      })
+      window.onresize = function () {
+        myChart.resize()
+      }
+    }
+
+
+    const open = (func) =>{
+      if (func === 'DATA_DISTRIBUTION'){
+        nextTick(() => {
+          drawChart()
+        })
+      }
+      else if (func === 'SCATTER_PLOT'){
+        nextTick(() => {
+          drawScatter()
+        })
+      }
+    }
+
+    return{
+      tableData,
+      visualizeVisible,
+      chart,
+      echarts,
+      echartsScatter,
+      scatterData,
+      scatterKey,
+      getData,
+      handleVisualize,
+      drawChart,
+      drawScatter,
+      open,
+    };
+  }
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+}
+.green {
+  color: rgb(32, 220, 88)
+}
+.yellow{
+  color:yellowgreen;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 402 - 0
src/views/analysis/AnalysisPipeline.vue

@@ -0,0 +1,402 @@
+<template>
+  <div>
+    <div class="crumbs">
+      <el-breadcrumb separator="/">
+        <el-breadcrumb-item>
+          <i class="el-icon-lx-cascades"></i> 数据分析流水线
+        </el-breadcrumb-item>
+      </el-breadcrumb>
+    </div>
+
+    <div class="container">
+      <el-table
+          :data="tableData" border class="table" ref="multipleTable"
+          header-cell-class-name="table-header"
+      >
+        <el-table-column prop="pipelineName" label="流水线名"></el-table-column>
+        <el-table-column prop="analysisFunction" label="分析方法"></el-table-column>
+        <el-table-column prop="chosenColumns" label="选中字段"></el-table-column>
+        <el-table-column label="操作" width="180" align="center">
+          <template #default="scope">
+            <el-button type="text" icon="el-icon-mouse"
+                       @click="handleUsePipeline(scope.row.id)">使用
+            </el-button>
+            <el-button type="text" icon="el-icon-delete" class="red"
+                       @click="handleDelete(scope.row.id)">删除</el-button>
+
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-dialog
+        v-model="usePipelineVisible"
+        title="流水线使用"
+        width="30%"
+        :before-close="handleClose"
+    >
+      <el-form :model="useForm" width="300px">
+        <el-form-item label="选择文件" >
+          <el-select v-model="useForm.file" :label-width="formLabelWidth" placeholder="请选择您要使用的文件">
+            <el-option
+                v-for="item in fileList.valueOf()"
+                :key="item.id"
+                :label="item.fileName"
+                :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="usePipelineVisible = false">取消</el-button>
+            <el-button type="primary" @click="goPipeline(useForm.file)" style="margin-left: 10px">使用流水线</el-button>
+          </span>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="visualizeVisible" title="可视化" @open="open(chart.analysisFunction)" width="700px">
+      <el-form :model="chart" width="400px">
+        <el-form-item label="分析函数: " >
+          {{chart.analysisFunction}}
+        </el-form-item>
+        <el-form-item label="分析结果: " v-if="chart.analysisFunction!=='SCATTER_PLOT'">
+          {{chart.analysisResult}}
+        </el-form-item>
+        <el-form-item label="可视化: " v-if="chart.analysisFunction === 'DATA_DISTRIBUTION'">
+          <div id="chartPie" style="width: 600px;height: 300px" ></div>
+        </el-form-item>
+        <el-form-item label="可视化: " v-if="chart.analysisFunction === 'SCATTER_PLOT'">
+          <div id="scatter" style="width: 600px;height: 300px" ></div>
+        </el-form-item>
+
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {inject, nextTick, reactive, ref} from "vue";
+import {deleteAnalysisPipeline, getAllAnalysisPipeline, useAnalysisPipeline} from "../../api/analysisPipeline";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {GetAllFile} from "../../api/file";
+
+export default {
+  name: "AnalysisPipeline",
+
+  setup(){
+    const tableData = ref();
+    let usePipelineVisible = ref(false)
+    let useForm = reactive({
+      file: "",
+    })
+    const formLabelWidth='120px'
+    const fileList = ref()
+    let usePipelineId = ref()
+    let visualizeVisible = ref(false)
+    const chart = reactive({
+      analysisFunction: '',
+      analysisResult: {},
+      pieResult: {},
+    })
+    let scatterData = ref([])
+    let scatterKey = ref([])
+
+    const echarts = inject('echarts')
+    const echartsScatter = inject('echarts')
+
+    const getPage = () => {
+      const uid=1;
+      GetAllFile(uid).then(res=>{
+        fileList.value=res.result.fileInfoVOList;
+      })
+      getAllAnalysisPipeline({uid}).then(res =>{
+        tableData.value = res.result.analysisPipelineVOList
+      })
+    }
+    getPage()
+
+    const handleUsePipeline = (pid) => {
+      usePipelineVisible.value = true
+      usePipelineId.value = pid
+    }
+    const goPipeline = (fileId) => {
+      usePipelineVisible.value = false
+      let data ={
+        pipelineId: usePipelineId.value,
+        fileId: fileId
+      }
+      ElMessageBox( {
+        title: "确定要使用流水线吗?",
+        showCancelButton: true,
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        beforeClose: (action, instance, done) => {
+          if (action === 'confirm') {
+            instance.confirmButtonLoading = true
+            instance.confirmButtonText = '分析中...'
+            useAnalysisPipeline(data).then(res=>{
+              instance.confirmButtonLoading = false
+              console.log("predic_res: ",res);
+              if (res.msg === '成功')
+              {
+                // ElMessageBox({
+                //   title: '流水线使用',
+                //   message: `流水线使用成功`,
+                //   confirmButtonText: '确定',
+                // })
+                done()
+                handleVisualize(res.result)
+              }
+              else
+              {
+                console.log(res);
+                ElMessage.error("分析失败!失败码:"+res.msg);
+                done()
+              }
+            }).catch(err=>{
+              console.log(err)
+            });
+          } else {
+            done()
+          }
+        },
+      })
+    }
+
+    const handleDelete = (pid) => {
+      ElMessageBox.confirm("确定要删除此流水线吗?", "提示", {
+        type: "warning",
+      }).then(()=>{
+        deleteAnalysisPipeline({pid}).then(res=>{
+          if(res.msg === '成功'){
+            ElMessage.success("删除成功");
+          }
+          else{
+            ElMessage.error("删除失败");
+          }
+        })
+      })
+    }
+    const handleVisualize = (result) => {
+      chart.analysisFunction = result.analysisFunction
+
+      if(chart.analysisFunction === 'SCATTER_PLOT'){
+        let tempArr= result.analysisResult
+        let tempObj = tempArr[0]
+        let out_arr = []
+        for(let key in tempObj){
+          out_arr.push(key)
+        }
+        scatterKey = out_arr
+        let tempScatterData = []
+        for (let i in tempArr){
+          let temp = []
+          for (let j in tempArr[i]){
+            temp.push(tempArr[i][j])
+          }
+          tempScatterData.push(temp)
+        }
+        scatterData = tempScatterData
+      }
+      else {
+        chart.analysisResult = result.analysisResult
+      }
+
+      visualizeVisible.value = true
+
+      if(chart.analysisFunction === 'DATA_DISTRIBUTION'){
+        let out_arr = []
+        let in_obj = chart.analysisResult
+        for(let key in in_obj){
+          let temp = {};			//创建临时对象
+          temp.name = key;		//存储对象的Key为name
+          temp.value = in_obj[key];	//存储value
+          out_arr.push(temp);
+        }
+        chart.pieResult = out_arr
+      }
+
+    }
+    const drawChart = () => {
+      let myChart = echarts.init(document.getElementById("chartPie"));
+      myChart.setOption({
+        //鼠标悬浮
+        tooltip: {
+          trigger: "item",
+          // formatter: "{a}<br/>{b}: <br/>{c}({d}%)",  其中 {a}指向name名称(访问来源)
+          formatter: "{b}: <br/>{c}({d}%)",
+        },
+        // legend: {
+        //   // data: ["直接访问", "邮件营销", "联盟广告", "视频广告", "搜索引擎"],
+        //   right: 300,
+        //   orient: "vertical",
+        //   // 下面注释的代码是控制分类放在哪个地方,需要体验的话,直接把上面的代码注释,把下面的代码解开注释即可
+        //     data: ["红", "黄", "绿"],
+        //     // left: "center",
+        //     // top: "bottom",
+        //     // orient: "horizontal"
+        // },
+        series: [
+          {
+            name: "???",
+            type: "pie",
+            //圆圈的粗细
+            radius: ["50%", "80%"],
+            //圆圈的位置
+            center: ["50%", "50%"],
+            data:  chart.pieResult,
+            animationDuration: 2000,
+            //控制是否显示指向文字的,默认为true
+            label: {
+              // show: false,
+              // position: "center",
+              //以下代码可以代表指向小文字的
+              show: true,
+              formatter: "{b} : {d}%",
+              textStyle: {
+                color: "#333",
+                fontSize: 14,
+              },
+            },
+          },
+        ],
+      })
+      window.onresize = function () {
+        myChart.resize()
+      }
+    }
+
+    const drawScatter = () => {
+      let myChart = echartsScatter.init(document.getElementById("scatter"));
+      myChart.setOption({
+        color: [
+          '#dd4444', '#fec42c', '#80F1BE'
+        ],
+        grid:{
+          right: '20%'
+        },
+        tooltip: {
+          //十字线
+          axisPointer: {
+            type: 'cross'
+          },
+          padding: 10,
+          backgroundColor: '#222',
+          borderColor: '#777',
+          borderWidth: 1,
+          //设置tooltip的显示内容
+          formatter: function (obj) {
+            let value = obj.value;
+            return '<div style="border-bottom: 1px solid rgba(255,255,255,.3); font-size: 18px;padding-bottom: 7px;margin-bottom: 7px">'
+                +  '具体数据:'
+                + '</div>'
+                + scatterKey[0] + ':' + value[0] + '<br>'
+                + scatterKey[1] + ':' + value[1] + '<br>'
+          }
+        },
+        xAxis: {
+          name: scatterKey[0],
+          splitLine: {
+            show: false
+          },
+        },
+        yAxis: {
+          name: scatterKey[1],
+          splitLine: {
+            show: false
+          }
+        },
+        series: [
+          {
+            type: 'scatter',
+            itemStyle: {
+              shadowBlur: 10,
+              shadowColor: 'rgba(120, 36, 50, 0.5)',
+              shadowOffsetY: 5,
+            },
+            data: scatterData,
+          }
+        ]
+      })
+      window.onresize = function () {
+        myChart.resize()
+      }
+    }
+
+
+    const open = (func) =>{
+      if (func === 'DATA_DISTRIBUTION'){
+        nextTick(() => {
+          drawChart()
+        })
+      }
+      else if (func === 'SCATTER_PLOT'){
+        nextTick(() => {
+          drawScatter()
+        })
+      }
+    }
+
+    return{
+      tableData,
+      usePipelineVisible,
+      useForm,
+      formLabelWidth,
+      fileList,
+      usePipelineId,
+      visualizeVisible,
+      chart,
+      scatterData,
+      scatterKey,
+      echarts,
+      echartsScatter,
+      getPage,
+      handleUsePipeline,
+      handleDelete,
+      goPipeline,
+      handleVisualize,
+      drawChart,
+      drawScatter,
+      open,
+    }
+  }
+
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+}
+.green {
+  color: rgb(32, 220, 88)
+}
+.yellow{
+  color:yellowgreen;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 119 - 73
src/views/file/FileDetail.vue

@@ -8,40 +8,35 @@
       </el-breadcrumb>
     </div>
     <div class="container">
-      <div class="handle-box" style="width:60%;">
+      <div class="handle-box">
         <el-button plain icon="el-icon-lx-file" @click="handleFileSelect" >选择文件</el-button>
 
-        <el-button type="success" icon="el-icon-search" @click="handleAnalysis">数理分析</el-button>
+        <el-button type="success" icon="el-icon-plus" @click="handleAnalysis">统计分析配置</el-button>
 
-        <el-button type="primary" icon="el-icon-plus" @click="createConfig">生成配置文件</el-button>
+        <el-button type="primary" icon="el-icon-plus" @click="createConfig">AI模型配置</el-button>
 
-        <el-button type="success" icon="el-icon-thumb" @click="getFeature">自动特征选择</el-button>
+        <el-button type="primary" icon="el-icon-thumb" @click="getFeature">自动特征选择</el-button>
 
       </div>
 
-      <el-table :data="tableData" border class="table" ref="fieldTable" header-cell-class-name="table-header" @selection-change="handleSelectChange">
-        <el-table-column width="50" type="selection" ></el-table-column>
+      <el-table :data="tableData"
+                border class="table"
+                ref="fieldTable"
+                header-cell-class-name="table-header"
+                @selection-change="handleSelectChange"
+      >
+        <el-table-column width="50" type="selection"></el-table-column>
+        <el-table-column label="字段id">
+          <template #default="scope"><span>{{ scope.row.id }}</span></template>
+        </el-table-column>
         <el-table-column label="字段名">
           <template #default="scope"><span>{{ scope.row.fieldName }}</span></template>
         </el-table-column>
-        <el-table-column label="别名">
-          <template #default="scope">{{ scope.row.aliasName }}</template>
-        </el-table-column>
-        <el-table-column label="描述">
-          <template #default="scope">{{ scope.row.fieldDes }}</template>
-        </el-table-column>
         <el-table-column label="取值类型">
-          <template #default="scope">{{ scope.row.fieldType }}</template>
-        </el-table-column>
-        <el-table-column label="分布类型">
-          <template #default="scope">{{ scope.row.conOrDis }}</template>
+          <template #default="scope">{{ scope.row.dataType }}</template>
         </el-table-column>
         <el-table-column label="空值率">
-          <template #default="scope">{{ scope.row.nullsRatio }}</template>
-        </el-table-column>
-
-        <el-table-column label="值域" >
-          <template #default="scope">{{ scope.row.valueInfo }}</template>
+          <template #default="scope">{{ scope.row.nullRate }}</template>
         </el-table-column>
       </el-table>
       <div class="pagination">
@@ -50,17 +45,20 @@
       </div>
     </div>
 
-    <el-dialog title="选择字段" v-model="analysisVisible" width="50%">
+    <el-dialog title="新建统计分析配置" v-model="analysisVisible" width="50%">
       <el-form label-width="120px">
-        <el-form-item label="分析方法" required>
-          <el-select v-model="form.type" placeholder="分析方法" class="handle-select mr10" @change="changeMethodMsg">
-            <el-option key="1" label="数据分布" value="1"></el-option>
-            <el-option key="2" label="散点图" value="2"></el-option>
-            <el-option key="3" label="数据统计" value="3"></el-option>
-            <el-option key="4" label="偏度-峰度" value="4"></el-option>
-            <el-option key="5" label="皮尔森相关系数" value="5"></el-option>
-          </el-select>
+        <el-form-item label="配置名" required>
+          <el-input v-model="analysisConfName"></el-input>
         </el-form-item>
+<!--        <el-form-item label="分析方法" required>-->
+<!--          <el-select v-model="form.type" placeholder="分析方法" class="handle-select mr10" @change="changeMethodMsg">-->
+<!--            <el-option key="1" label="数据分布" value="1"></el-option>-->
+<!--            <el-option key="2" label="散点图" value="2"></el-option>-->
+<!--            <el-option key="3" label="数据统计" value="3"></el-option>-->
+<!--            <el-option key="4" label="偏度-峰度" value="4"></el-option>-->
+<!--            <el-option key="5" label="皮尔森相关系数" value="5"></el-option>-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
         <el-alert type="info"
                   show-icon style="height: 40px;">{{methodMsg.valueOf()}}</el-alert>
         <el-form-item label="方法和字段要求" required>
@@ -77,13 +75,13 @@
       <template #footer>
                 <span class="dialog-footer">
                     <el-button @click="analysisVisible = false">取 消</el-button>
-                    <el-button type="primary" @click="startAnalysis">确 定</el-button>
+                    <el-button type="primary" @click="createAnalysisConfig()">确 定</el-button>
                 </span>
       </template>
     </el-dialog>
 
     <!--数理函数分析结果-->
-    <el-dialog title="分析结果" v-model="resultVisible" width="50%">
+    <el-dialog title="数理函数分析结果" v-model="resultVisible" width="50%">
       <el-form label-width="120px">
         <el-form-item label="分析方法" required>
             {{ AnalysisMethodName[parseInt(form.type)-1] }}
@@ -113,34 +111,48 @@
       </template>
     </el-dialog>
 
-    <el-dialog title="新建配置" v-model="configCreate" width="50%">
+    <el-dialog title="新建AI模型配置" v-model="configCreate" width="50%">
       <el-form label-width="120px">
         <el-form-item label="配置名" required>
           <el-input v-model="form.name"></el-input>
         </el-form-item>
+<!--        <el-form-item label="模型类别" required>-->
+<!--          <el-select v-model="form.type" placeholder="模型类别" class="handle-select mr10" @change="selectModelTypeChange">-->
+<!--            <el-option key="1" label="逻辑回归" value="1"></el-option>-->
+<!--            <el-option key="2" label="决策树" value="2"></el-option>-->
+<!--            <el-option key="3" label="随机森林" value="3"></el-option>-->
+<!--            <el-option key="4" label="梯度提升决策树" value="4"></el-option>-->
+<!--            <el-option key="5" label="K-均值" value="5"></el-option>-->
+<!--            <el-option key="6" label="多层感知器分类器" value="6"></el-option>-->
+<!--            <el-option key="7" label="朴素贝叶斯" value="7"></el-option>-->
+<!--            <el-option key="8" label="随机森林回归" value="8"></el-option>-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
         <el-form-item label="模型类别" required>
-          <el-select v-model="form.type" placeholder="模型类别" class="handle-select mr10" @change="selectModelTypeChange">
-            <el-option key="1" label="逻辑回归" value="1"></el-option>
-            <el-option key="2" label="决策树" value="2"></el-option>
-            <el-option key="3" label="随机森林" value="3"></el-option>
-            <el-option key="4" label="梯度提升决策树" value="4"></el-option>
-            <el-option key="5" label="K-均值" value="5"></el-option>
-            <el-option key="6" label="多层感知器分类器" value="6"></el-option>
-            <el-option key="7" label="朴素贝叶斯" value="7"></el-option>
-            <el-option key="8" label="随机森林回归" value="8"></el-option>
+          <el-select v-model="form.type" placeholder="学习类型" class="handle-select mr10">
+            <el-option key="1" label="监督学习" value="SUPERVISED_LEARNING"></el-option>
+            <el-option key="2" label="非监督学习" value="UNSUPERVISED_LEARNING"></el-option>
           </el-select>
+          <el-alert type="info" description="您选择的最后一个字段为标签字段" v-if="form.type === 'SUPERVISED_LEARNING'"
+                    show-icon style="height: 40px; margin-top: 10px"></el-alert>
+          <el-alert type="info" description="没有目标字段,适用于k-means模型" v-if="form.type === 'UNSUPERVISED_LEARNING'"
+                    show-icon style="height: 40px; margin-top: 10px"></el-alert>
         </el-form-item>
-        <el-form-item label="已选择字段" >
-          <el-alert type="info" description="您下列选择的最后一个字段为分类模型的目标字段" v-if="featureTip===1"
-                    show-icon style="height: 40px;"></el-alert>
-          <el-alert type="info" description="k-means模型没有目标字段" v-if="featureTip===2"
-                    show-icon style="height: 40px;"></el-alert>
-          <el-table :data="selectData" border class="table"  header-cell-class-name="table-header" >
+
+        <el-form-item label="选中字段" >
+          <el-table :data="selectData" border class="table" header-cell-class-name="table-header" >
             <el-table-column label="字段名">
               <template #default="scope"><span>{{ scope.row.fieldName }}</span></template>
             </el-table-column>
           </el-table>
         </el-form-item>
+<!--        <el-form-item label="标签字段" v-if="labelTip===1">-->
+<!--          <el-table :data="selectData" border class="table" header-cell-class-name="table-header">-->
+<!--            <el-table-column label="字段名">-->
+<!--              <template #default="scope"><span>{{ scope.row.fieldName }}</span></template>-->
+<!--            </el-table-column>-->
+<!--          </el-table>-->
+<!--        </el-form-item>-->
 
       </el-form>
       <template #footer>
@@ -162,6 +174,7 @@ import {useRoute}from "vue-router";
 import {useRouter}from "vue-router";
 import { toRaw } from '@vue/reactivity'
 import {addConfig,getFeatureSelection} from "../../api/config";
+import {addAnalysisConfig} from "../../api/analysisConfig";
 
 export default {
   name: "config",
@@ -201,25 +214,16 @@ export default {
     // 获取表格数据
     const getData = () => {
       const route=useRoute()
-      //todo:调用获取文件具体信息获取字段列表
       if(route.query.fileId!=null) {
         getFileDetail(route.query.fileId).then(res => {
-          tableData.value = res.data.headerInfos;
-          for (var i = 0; i < tableData.value.length; i++) {
-            if (tableData.value[i].conOrDis === 1) {
-              tableData.value[i].conOrDis = "连续值"
-            } else {
-              tableData.value[i].conOrDis = "离散值"
-            }
-          }
-          //console.log(tableData)
-          console.log("fileId="+route.query.fileId)
+          tableData.value = res.result.fileFieldList;
           fileId=route.query.fileId
         })
       }
 
     };
     getData();
+
     const getFeature=()=>{
       if(selectData.value.length===0){ElMessage.error("请先选择目标字段");return;}
       if(selectData.value.length>1){ElMessage.error("目标字段仅可选择一个");return;}
@@ -265,14 +269,40 @@ export default {
     let form = reactive({
       name: "",
       type: "",
+      needLabel: "",
       // classification:"",
       // feature:"",
     });
+    let analysisConfName = ref();
 
     let idx = -1;
     const handleAnalysis = () => {
+      if (selectData.value.length<1){
+        ElMessage.warning("至少选择一个字段!");
+        return ;
+      }
       analysisVisible.value = true;
     };
+    const createAnalysisConfig = () => {
+      const payload={
+        fileId:selectData.value[0].fileInfoId,
+        userId:1,
+        configName:analysisConfName,
+        chosenColumns:[],
+      }
+        for(let i=0;i<selectData.value.length;i++){
+          payload.chosenColumns.push(selectData.value[i].fieldName);
+        }
+        console.log(payload)
+      addAnalysisConfig(payload).then(res=>{
+        if(res.msg === '成功') {
+          ElMessage.success(`已成功生成配置`);
+          analysisVisible.value = false;
+        }
+        else ElMessage.error(`生成配置失败`);
+      })
+    };
+
     const changeMethodMsg=(value)=>{
       console.log(value)
       if(value===1){
@@ -354,19 +384,19 @@ export default {
         }
       });
 
-      // ElMessage.success(`修改第 ${idx + 1} 行成功`);
-      // Object.keys(form).forEach((item) => {
-      //   tableData.value[idx][item] = form[item];
-      // });
-
     };
     let resultVisible= ref(false);
-  let featureTip=ref(0);
+    let featureTip=ref(0);
+    let labelTip = ref(0);
+
     const selectModelTypeChange=(selectValue)=>{
       if(selectValue==="8")featureTip.value=2;
       else featureTip.value=1;
-      console.log(featureTip.value)
     }
+    // const selectLabelChange=(selectValue)=>{
+    //   if(selectValue === "SUPERVISED_LEARNING") labelTip.value=1;
+    //   else labelTip.value=2;
+    // }
     const configCreate = ref(false);
     const createConfig =() => {
       if (selectData.value.length<=1){
@@ -374,24 +404,36 @@ export default {
         return ;
       }
       const fieldTable = ref(null)
-      //console.log(fieldTable)
-      console.log(selectData.value)
       configCreate.value = true;
     };
     const saveConfig=()=>{
       const route=useRoute()
-      const payload={fileInfoId:selectData.value[0].fileInfoId,userId:1, modelTypeId:form.type, confName:form.name , fieldIds:[]}
-      for(let i=0;i<selectData.value.length;i++){
-        payload.fieldIds.push(selectData.value[i].id);
+      const payload={
+        fileInfoId:selectData.value[0].fileInfoId,
+        userId:1,
+        learningType:form.type,
+        configName:form.name,
+        features:[],
+        label: ''
+      }
+      if (form.type === 'UNSUPERVISED_LEARNING'){
+        for(let i=0;i<selectData.value.length;i++){
+          payload.features.push(selectData.value[i].fieldName);
+        }
+      }
+      else{
+        for(let i=0;i<selectData.value.length-1;i++){
+          payload.features.push(selectData.value[i].fieldName);
+        }
+        payload.label = selectData.value[selectData.value.length-1].fieldName
       }
       addConfig(payload).then(res=>{
-        if(res.status===200) {
+        if(res.msg === '成功') {
           ElMessage.success(`已成功生成配置`);
           configCreate.value = false;
         }
         else ElMessage.error(`生成配置失败`);
       })
-      console.log(payload)
     }
     const handleSelectChange=val=>{
       selectData.value=val;
@@ -405,6 +447,7 @@ export default {
       analysisVisible,
       resultVisible,
       form,
+      analysisConfName,
       configCreate,
       createConfig,
       saveConfig,
@@ -412,8 +455,11 @@ export default {
       selectData,
       analysisData,
       featureTip,
+      labelTip,
       methodMsg,
       selectModelTypeChange,
+      //selectLabelChange,
+      createAnalysisConfig,
       handleSelectChange,
       handleSearch,
       handlePageChange,

+ 35 - 34
src/views/file/FileList.vue

@@ -18,30 +18,30 @@
       <el-table
               :data="state.fileList"
               :show-header="true"
-              style="width: 100%; margin-top: 20px"
-      >
-        <el-table-column label="文件名">
-          <template #default="scope">
-            <span class="message-title">{{scope.row.fileName}}</span>
-          </template>
-        </el-table-column>
-
-        <el-table-column label="操作" width="300">
-          <template #default="scope">
-            <!----------------------------todo------------------------------->
-            <!----------------------------获取文件详细信息、跳转到响应页面;删除文件------------------------------->
-            <div style="display:flex; width: 100%;">
-              <el-button size="small" style="margin-right: 10px"
-                         @click="handleFileDetail(scope.row.fileId)">查看</el-button>
-              <el-button size="small" type="warning" style="margin-right: 10px"
-                         @click="handleShareDialog(scope.row.fileId);shareDialogVisible=true">分享</el-button>
-            <!--  scope.$index 是数组下标,这里再传一个fileId             handleFileDelete(scope.$index,scope.row.fileId)   -->
-              <el-button size="small" type="danger"
-                         @click="handleDeleteDialog(scope.row.fileId);deleteDialogVisible=true">删除</el-button>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
+              style="width: 100%; margin-top: 20px">
+
+            <el-table-column label="文件名">
+              <template #default="scope">
+                <span class="message-title">{{scope.row.fileName}}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column width="300" label="操作">
+              <template #default="scope">
+                <!----------------------------todo------------------------------->
+                <!----------------------------获取文件详细信息、跳转到响应页面;删除文件------------------------------->
+                <div style="display:flex; width: 100%;">
+                  <el-button size="small" style="margin-right: 10px"
+                             @click="handleFileDetail(scope.row.fileId)">查看</el-button>
+                  <el-button size="small" type="warning" style="margin-right: 10px"
+                             @click="handleShareDialog(scope.row.fileId);shareDialogVisible=true">分享</el-button>
+                  <!--  scope.$index 是数组下标,这里再传一个fileId             handleFileDelete(scope.$index,scope.row.fileId)   -->
+                  <el-button size="small" type="danger"
+                             @click="handleDeleteDialog(scope.row.fileId);deleteDialogVisible=true">删除</el-button>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
     </div>
 
     <el-dialog
@@ -64,10 +64,10 @@
     </el-dialog>
 
     <el-dialog
-        v-model="deleteDialogVisible"
-        title="确认删除"
-        width="30%"
-        top="30vh"
+            v-model="deleteDialogVisible"
+            title="确认删除"
+            width="30%"
+            top="30vh"
     >
       <span>确认要删除该文件吗?</span>
       <template #footer>
@@ -109,7 +109,6 @@ export default {
         }
       ],
     });
-
     let shareDialogVisible=ref(false);
     let toShareFileId=ref(0);
 
@@ -119,8 +118,9 @@ export default {
     //用户Id => 文件列表
     const getFileList=(userId)=>{
       GetAllFile(userId).then(res=>{
-        if(res.status===200){
-          state.fileList=res.data;
+        if(res.msg === '成功'){
+          state.fileList=res.result.fileInfoVOList;
+          console.log(state.fileList)
         }
         else{
           console.log("getFileList Error!");
@@ -159,24 +159,25 @@ export default {
       })
     }
 
+
     //打开提示确认删除的对话框,将fileId保存下来
     const handleDeleteDialog = (fileId)=>{
       toDeleteFileId=fileId;
       console.log("handleDeleteDialog")
       console.log("fileId:"+fileId)
-      //ElMessage.info("要删除的文件Id fileId:"+fileId)
+      ElMessage.info("要删除的文件Id fileId:"+fileId)
     }
 
     //删除对应文件
     const handleFileDelete = () => {
       console.log("fileId:"+toDeleteFileId)
       deleteFile(toDeleteFileId).then(res=>{
-        if(res.status===200){
+        if(res.msg === '成功'){
           ElMessage.success("删除成功!")
           getFileList(userId);
         }
         else{
-          ElMessage.error("删除失败!错误码:"+res.status)
+          ElMessage.error("删除失败!错误码:"+res.msg)
         }
       })
     };

+ 1 - 1
src/views/file/Upload.vue

@@ -186,7 +186,7 @@ export default {
             csvForm.fileName=''
             formEl.resetFields()
             uploadEl.clearFiles()
-            if (res.status === 200)
+            if (res.msg === '成功')
             {
               ElMessageBox({
                 title: '文件上传',

+ 113 - 150
src/views/ConfigDetail.vue → src/views/ml/ConfigDetail.vue

@@ -8,50 +8,18 @@
       </el-breadcrumb>
     </div>
 
-      <div class="handle-box">
-
-
-
-      </div>
-
 <!--  <el-tab-pane :label="featureList"> </el-tab-pane>-->
       <div class="container">
         <div class="form-box" >
           <el-form ref="formRef" label-width="120px">
-            <el-form-item label="模型类型名:" >
-              {{config.modelTypeName}}
-            </el-form-item>
-            <el-form-item label="模型描述:" >
-              {{config.modelDes}}
-            </el-form-item>
-            <el-form-item label="模型详情:" >
-              {{config.modelDetileDes}}
+            <el-form-item label="学习类型:" >
+              {{config.learningType}}
             </el-form-item>
             <el-form-item label="特征字段:" >
-              <el-table :data="tableData" border class="table"  ref="fieldTable" header-cell-class-name="table-header" @selection-change="handleSelectChange">
-                <el-table-column label="字段名" width="120%">
-                  <template #default="scope"><span>{{ scope.row.fieldName }}</span></template>
-                </el-table-column>
-                <el-table-column label="别名">
-                  <template #default="scope">{{ scope.row.aliasName }}</template>
-                </el-table-column>
-                <el-table-column label="描述">
-                  <template #default="scope">{{ scope.row.fieldDes }}</template>
-                </el-table-column>
-                <el-table-column label="取值类型">
-                  <template #default="scope">{{ scope.row.fieldType }}</template>
-                </el-table-column>
-                <el-table-column label="分布类型">
-                  <template #default="scope">{{ scope.row.conOrDis }}</template>
-                </el-table-column>
-                <el-table-column label="空值率">
-                  <template #default="scope">{{ scope.row.nullsRatio }}</template>
-                </el-table-column>
-
-                <el-table-column label="值域" width="150%">
-                  <template #default="scope">{{ scope.row.valueInfo }}</template>
-                </el-table-column>
-              </el-table>
+              {{config.features}}
+            </el-form-item>
+            <el-form-item label="目标字段:" >
+              {{config.label}}
             </el-form-item>
 
             <el-form-item label="模型列表:" >
@@ -59,13 +27,18 @@
               <el-table-column label="模型名" width="150%">
                 <template #default="scope"><span>{{ scope.row.modelName }}</span></template>
               </el-table-column>
-              <el-table-column label="模型类别" width="150%">
-                <template #default="scope">{{ scope.row.modelDetailName }}</template>
+              <el-table-column label="机器学习算法" width="150%">
+                <template #default="scope">
+                  {{ scope.row.machineLearningAlgorithm }}
+                </template>
               </el-table-column>
               <el-table-column label="训练状态">
                 <template #default="scope" >
-                  <div v-if="scope.row.trainedStatus===-1">未训练</div>
-                  <div v-else="">已训练</div></template>
+                  <div v-if="scope.row.trainingState==='UNTRAINED'">未训练</div>
+                  <div v-else-if="scope.row.trainingState==='TRAINING'">训练中</div>
+                  <div v-else-if="scope.row.trainingState==='SUCCESS'">训练成功</div>
+                  <div v-else="">训练失败</div>
+                </template>
               </el-table-column>
               <el-table-column label="操作" width="180" align="center">
                 <template #default="scope">
@@ -92,32 +65,41 @@
 
           <el-dialog title="新建模型" v-model="modelVisiable" width="30%">
             <el-form label-width="120px">
-              <el-form-item label="文件Id" required>
-                <el-input v-model="form.fileInfoId" ></el-input>
-              </el-form-item>
               <el-form-item label="模型名称" required>
                 <el-input v-model="form.modelName"></el-input>
               </el-form-item>
-              <el-form-item label="MaxIter" v-model="showMaxIter" v-if="showMaxIter===true">
-                <el-input v-model="form.MaxIter"></el-input>
+              <el-form-item label="算法名称" required>
+                <el-select v-model="form.machineLearningAlgorithm" placeholder="请选择一个算法" class="handle-select mr10">
+                  <el-option key="1" label="逻辑回归" value="LOGISTIC_REGRESSION_CLASSIFICATION"></el-option>
+                  <el-option key="2" label="决策树" value="DECISION_TREE_CLASSIFICATION"></el-option>
+                  <el-option key="3" label="梯度提升树分类" value="GRADIENT_BOOSTED_TREE_CLASSIFICATION"></el-option>
+                  <el-option key="4" label="多层感知器分类" value="MULTI_LAYER_PERCEPTRON_CLASSIFICATION"></el-option>
+                  <el-option key="5" label="朴素贝叶斯分类" value="NAIVE_BAYES_CLASSIFICATION"></el-option>
+                  <el-option key="6" label="随机森林分类" value="RANDOM_FOREST_CLASSIFICATION"></el-option>
+                  <el-option key="7" label="随机森林回归" value="RANDOM_FOREST_REGRESSION"></el-option>
+                  <el-option key="8" label="K均值聚类" value="K_MEANS_CLUSTERING"></el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="MaxIter" v-model="showMaxIter" v-if="judgeAlgorithmType() === 1">
+                <el-input v-model="form.trainParams.MaxIter"></el-input>
               </el-form-item>
-              <el-form-item label="RegParam" v-model="showRegParam" v-if="showRegParam===true">
-                <el-input v-model="form.RegParam"></el-input>
+              <el-form-item label="RegParam" v-model="showRegParam" v-if="judgeAlgorithmType() === 1">
+                <el-input v-model="form.trainParams.RegParam"></el-input>
               </el-form-item>
-              <el-form-item label="ElasticNetParam" v-model="showElasticNetParam" v-if="showElasticNetParam===true">
-                <el-input v-model="form.ElasticNetParam"></el-input>
+              <el-form-item label="ElasticNetParam" v-model="showElasticNetParam" v-if="judgeAlgorithmType() === 1">
+                <el-input v-model="form.trainParams.ElasticNetParam"></el-input>
               </el-form-item>
-              <el-form-item label="Category" v-model="showCategory" v-if="showCategory===true">
-                <el-input v-model="form.Category"></el-input>
+              <el-form-item label="Category" v-model="showCategory" v-if="judgeAlgorithmType() === 3">
+                <el-input v-model="form.trainParams.Category"></el-input>
               </el-form-item>
-              <el-form-item label="DataSetOccupy" v-model="showTrainingDataSetOccupy" v-if="showTrainingDataSetOccupy===true">
-                <el-input v-model="form.TrainingDataSetOccupy"></el-input>
+              <el-form-item label="DataSetOccupy" v-model="showTrainingDataSetOccupy" v-if="judgeAlgorithmType() === 3">
+                <el-input v-model="form.trainParams.TrainingDataSetOccupy"></el-input>
               </el-form-item>
-              <el-form-item label="NumOfCluster" v-model="showNumOfCluster" v-if="showNumOfCluster===true">
-                <el-input v-model="form.NumOfCluster"></el-input>
+              <el-form-item label="NumOfCluster" v-model="showNumOfCluster" v-if="judgeAlgorithmType() === 2">
+                <el-input v-model="form.trainParams.NumOfCluster"></el-input>
               </el-form-item>
-              <el-form-item label="Seed" v-model="showSeed" v-if="showSeed===true">
-                <el-input v-model="form.Seed"></el-input>
+              <el-form-item label="Seed" v-model="showSeed" v-if="judgeAlgorithmType() === 2">
+                <el-input v-model="form.trainParams.Seed"></el-input>
               </el-form-item>
 
             </el-form>
@@ -151,11 +133,12 @@
 <script>
 import {ref, reactive, getCurrentInstance, onMounted} from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { fetchData } from "../api/index";
-import {getFileDetail} from "../api/file";
-import {getOneConfig} from "../api/config";
+import { fetchData } from "../../api";
+import {getFileDetail} from "../../api/file";
+import {getOneConfig} from "../../api/config";
 import {useRoute}from "vue-router";
-import {addModel} from "../api/model";
+import {addModel, getModelByConfigId} from "../../api/model";
+import {getEnum} from "../../api/utils";
 export default {
   name: "configDetail",
   //训练特征
@@ -192,74 +175,64 @@ export default {
     const showNumOfCluster=ref(false);
     const showSeed=ref(false);
 
+    // let form = reactive({
+    //   modelTypeId:"",
+    //   configId:route.query.configId,
+    //   fileInfoId:"",
+    //   modelName:"",
+    //   MaxIter:"",
+    //   RegParam:"",
+    //   ElasticNetParam:"",
+    //   Category:"",
+    //   TrainingDataSetOccupy:"",
+    //   NumOfCluster:"",
+    //   Seed:"",
+    // });
     let form = reactive({
-      modelTypeId:"",
-      configId:route.query.configId,
-      fileInfoId:"",
-      modelName:"",
-      MaxIter:"",
-      RegParam:"",
-      ElasticNetParam:"",
-      Category:"",
-      TrainingDataSetOccupy:"",
-      NumOfCluster:"",
-      Seed:"",
-
-
-
-
-
+      configId: route.query.configId,
+      modelName: "",
+      machineLearningAlgorithm: "",
+      trainParams: {},
     });
     // 获取表格数据
 
     const getData = () => {
-
-      //todo:调用获取文件具体信息获取字段列表
       // console.log("queryId",route.query.configId);
-
       if(route.query.configId!=null) {
         getOneConfig({configId:route.query.configId}).then(res => {
           // console.log("configdetail",route.query.configId);
-
-          config.value=res.data;
-          form.modelTypeId=config.value.modelTypeId;
-          if (form.modelTypeId==='1') {
-            showMaxIter.value = true;
-            showRegParam.value = true;
-            showElasticNetParam.value = true;
-          }else if (form.modelTypeId==='5') {
-            showNumOfCluster.value=true;
-            showSeed.value=true;
-          }else {
-            showCategory.value=true;
-            showTrainingDataSetOccupy.value=true;
-          }
-          console.log("form",form);
-          //console.log("config",config.value.modelTypeId);
-          tableData.value = res.data.featureList;
-          for (var i = 0; i < tableData.value.length; i++) {
-            if (tableData.value[i].conOrDis === 1) {
-              tableData.value[i].conOrDis = "连续值"
-            } else {
-              tableData.value[i].conOrDis = "离散值"
-            }
-          }
-          //console.log(tableData)
-
-          modelData.value=res.data.modelInfos;
-          console.log("tableData",tableData);
-          fileId.value=tableData.value[0].fileInfoId;
-          form.fileInfoId=(String)(fileId.value)
+          config.value=res.result;
+          // form.modelTypeId=config.value.learningType;
+          // if (form.modelTypeId==='1') {
+          //   showMaxIter.value = true;
+          //   showRegParam.value = true;
+          //   showElasticNetParam.value = true;
+          // }else if (form.modelTypeId==='5') {
+          //   showNumOfCluster.value=true;
+          //   showSeed.value=true;
+          // }else {
+          //   showCategory.value=true;
+          //   showTrainingDataSetOccupy.value=true;
+          // }
+          // console.log("form",form);
+          tableData.value = res.result.features;
+        });
+        getModelByConfigId({configId:route.query.configId}).then(res =>{
+          modelData.value=res.result.modelVOList;
         })
-
+        // getEnum().then(res=>{
+        //   console.log("enum: "+res)
+        // })
       }
 
     };
     getData();
 
-
-
-
+    const judgeAlgorithmType =()=>{
+      if (form.machineLearningAlgorithm === 'LOGISTIC_REGRESSION_CLASSIFICATION') return 1;
+      else if (form.machineLearningAlgorithm === 'K_MEANS_CLUSTERING') return 2;
+      else return 3;
+    }
 
     // 查询操作
     const handleSearch = () => {
@@ -274,58 +247,47 @@ export default {
 
     // 删除操作
 
-
     // 表格编辑时弹窗和保存
     const editVisible = ref(true);
 
-
-
     const saveModel = () => {
       modelVisiable.value = false
-      form.fileInfoId=tableData.value[0].fileInfoId
-      console.log(form.fileInfoId)
-      //todo:   调用Model相关接口
-      console.log("savemodel",form);
-      const payload={modelTypeId:form.modelTypeId,
-                     configId:form.configId,
-                      fileInfoId:form.fileInfoId,
-                      modelName:form.modelName,
-                      argument:[]
+      const payload={
+        configId:form.configId,
+        userId: 1,
+        modelName:form.modelName,
+        machineLearningAlgorithm: form.machineLearningAlgorithm,
+        trainParams:{}
       }
       const arg1={
-        MaxIter:form.MaxIter,
-        RegParam:form.RegParam,
-        ElasticNetParam:form.ElasticNetParam
+        MaxIter:form.trainParams.MaxIter,
+        RegParam:form.trainParams.RegParam,
+        ElasticNetParam:form.trainParams.ElasticNetParam
       }
       const arg3={
-        Category:form.Category,
-        TrainingDataSetOccupy:form.TrainingDataSetOccupy
+        Category:form.trainParams.Category,
+        TrainingDataSetOccupy:form.trainParams.TrainingDataSetOccupy
       }
       const arg4={
-        NumOfCluster:form.NumOfCluster,
-        Seed:form.Seed
+        NumOfCluster:form.trainParams.NumOfCluster,
+        Seed:form.trainParams.Seed
       }
-      if (config.value.modelTypeId==='1'){
-        payload.argument=arg1;
-      }else if (config.value.modelTypeId==='5'){
-        payload.argument=arg4;
+      if (judgeAlgorithmType() === 1){
+        payload.trainParams=arg1;
+      }else if (judgeAlgorithmType() === 5){
+        payload.trainParams=arg4;
       }else{
-        payload.argument=arg3;
+        payload.trainParams=arg3;
       }
-      console.log("here",payload);
       addModel(payload).then(res=>{
-
-        if(res.status===200) {
+        console.log(res)
+        if(res.msg === '成功') {
           ElMessage.success(`已成功生成模型`);
-
         }
         else ElMessage.error(`生成模型失败`);
       })
 
 
-
-
-
     };
     let idx = -1;
     const handleEdit = () => {
@@ -336,10 +298,9 @@ export default {
 
     //生成模型弹窗
     const modelVisiable=ref(false);
-    const createModel=()=>{
 
+    const createModel=()=>{
         modelVisiable.value=true;
-      console.log("here",modelVisiable);
     }
     const handleSelectChange=val=>{
       selectData.value=val;
@@ -364,6 +325,8 @@ export default {
       showNumOfCluster,
       showSeed,
       showTrainingDataSetOccupy,
+
+      judgeAlgorithmType,
       selectData,
       handleSelectChange,
       handleSearch,
@@ -388,7 +351,7 @@ export default {
 }
 
 .handle-select {
-  width: 120px;
+  width: 150px;
 }
 
 .handle-input {

+ 0 - 0
src/views/ConfigForm.vue → src/views/ml/ConfigForm.vue


+ 24 - 24
src/views/ConfigList.vue → src/views/ml/ConfigList.vue

@@ -36,21 +36,21 @@
                 @selection-change="handleSelectionChange"
                 @row-dblclick="getConfigDetail"
             >
-                <el-table-column prop="confName" label="配置名"></el-table-column>
-                <el-table-column label="模型类别">
-                    <template #default="scope">{{ scope.row.modelTypeName }}</template>
+                <el-table-column prop="configName" label="配置名"></el-table-column>
+                <el-table-column label="学习类型">
+                    <template #default="scope">{{ scope.row.learningType }}</template>
                 </el-table-column>
-              <el-table-column prop="fileName" label="文件名"></el-table-column>
+              <el-table-column prop="fileInfoId" label="文件id"></el-table-column>
                 <el-table-column label="特征字段">
-                    <template #default="scope">{{ scope.row.name }}</template>
+                    <template #default="scope">{{ scope.row.features }}</template>
                 </el-table-column>
               <el-table-column label="目标字段">
-                <template #default="scope">{{ scope.row.target }}</template>
+                <template #default="scope">{{ scope.row.label }}</template>
               </el-table-column>
                 <el-table-column label="操作" width="180" align="center">
                     <template #default="scope">
 
-                      <router-link :to="{path:'/ConfigDetail',query:{configId:scope.row.configId}}">
+                      <router-link :to="{path:'/ConfigDetail',query:{configId:scope.row.id}}">
                         <el-button type="text" icon="el-icon-view" >查看详情
                         </el-button>
                       </router-link>
@@ -117,8 +117,8 @@
 <script>
 import { ref, reactive } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { fetchData } from "../api/index";
-import {addConfig, deleteModelByModelId, getAllConfig} from "../api/config";
+import { fetchData } from "../../api";
+import {addConfig, deleteConfig, getAllConfig} from "../../api/config";
 import {useRouter} from "vue-router";
 
 export default {
@@ -163,19 +163,17 @@ export default {
         // 获取表格数据
 
     const getpage=()=> {
-      // console.log("getpage");
       getAllConfig({
         uid: 1
       }).then(res => {
-        console.log("res",res);
-        tableData.value = res.data;
-        for (let i=0;i<tableData.value.length;i++){
-          tableData.value[i].name=[];
-          for (let j=0;j<tableData.value[i].featureList.length-1;j++){
-            tableData.value[i].name.push(tableData.value[i].featureList[j].fieldName);
-          }
-          tableData.value[i].target=tableData.value[i].featureList[tableData.value[i].featureList.length-1].fieldName;
-        }
+        tableData.value = res.result.configVOList;
+        // for (let i=0;i<tableData.value.length;i++){
+        //   tableData.value[i].name=[];
+        //   for (let j=0;j<tableData.value[i].featureList.length-1;j++){
+        //     tableData.value[i].name.push(tableData.value[i].featureList[j].fieldName);
+        //   }
+        //   tableData.value[i].target=tableData.value[i].featureList[tableData.value[i].featureList.length-1].fieldName;
+        // }
       })
     };
      getpage();
@@ -202,14 +200,15 @@ export default {
                 .then(() => {
                     // ElMessage.success("删除成功");
                     // tableData.value.splice(index, 1);
-                  console.log("删除");
-                  let idx=tableData.value[index].configId;
+                  let idx=tableData.value[index].id;
                   console.log(idx);
-                  deleteModelByModelId(idx
+                  deleteConfig(idx
                   ).then(res=>{
                     //console.log("deleteres",res);
-                    if(res.status===200){
-                      location.reload();
+                    if(res.msg === '成功'){
+                      ElMessage.success("删除成功!")
+                      //location.reload();
+                      getpage();
                     }
                   });
 
@@ -334,6 +333,7 @@ export default {
 }
 .red {
     color: #ff0000;
+    margin-left: 10px;
 }
 .mr10 {
     margin-right: 10px;

+ 221 - 0
src/views/ml/MLPipeline.vue

@@ -0,0 +1,221 @@
+<template>
+<div>
+  <div class="crumbs">
+    <el-breadcrumb separator="/">
+      <el-breadcrumb-item>
+        <i class="el-icon-lx-cascades"></i> 机器学习流水线
+      </el-breadcrumb-item>
+    </el-breadcrumb>
+  </div>
+
+  <div class="container">
+    <el-table
+        :data="tableData" border class="table" ref="multipleTable"
+        header-cell-class-name="table-header"
+    >
+      <el-table-column prop="pipelineName" label="流水线名"></el-table-column>
+      <el-table-column prop="features" label="特征字段"></el-table-column>
+      <el-table-column prop="label" label="标签字段"></el-table-column>
+      <el-table-column label="模型算法">
+        <template #default="scope">{{ scope.row.machineLearningAlgorithm.algorithmName }}</template>
+      </el-table-column>
+      <el-table-column prop="arguments" label="参数值">
+        <template #default="scope">
+          <div v-for="(item,index) in scope.row.trainParams" :key="index">{{item}}</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+            <el-button type="text" icon="el-icon-mouse"
+                       @click="handleUsePipeline(scope.row.id)">使用
+            </el-button>
+            <el-button type="text" icon="el-icon-delete" class="red"
+                     @click="handleDelete(scope.row.id)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+
+  <el-dialog
+      v-model="usePipelineVisible"
+      title="流水线使用"
+      width="30%"
+      :before-close="handleClose"
+  >
+    <el-form :model="useForm" width="300px">
+      <el-form-item label="选择文件" >
+        <el-select v-model="useForm.file" :label-width="formLabelWidth" placeholder="请选择您要使用的文件">
+          <el-option
+              v-for="item in fileList.valueOf()"
+              :key="item.id"
+              :label="item.fileName"
+              :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="usePipelineVisible = false">取消</el-button>
+            <el-button type="primary" @click="goPipeline(useForm.file)" style="margin-left: 10px">使用流水线</el-button>
+          </span>
+    </template>
+  </el-dialog>
+
+</div>
+</template>
+
+<script>
+import {reactive, ref} from "vue";
+import {deletePipeline, getAllPipeline, usePipeline} from "../../api/pipeline";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {GetAllFile} from "../../api/file";
+
+export default {
+  name: "MLPipeline",
+  setup(){
+    const tableData = ref();
+    let usePipelineVisible = ref(false)
+    let useForm = reactive({
+      file: "",
+    })
+    const formLabelWidth='120px'
+    const fileList = ref()
+    let usePipelineId = ref()
+
+    const getPage = () => {
+      const uid=1;
+      GetAllFile(uid).then(res=>{
+        fileList.value=res.result.fileInfoVOList;
+      })
+      getAllPipeline({uid}).then(res =>{
+        tableData.value = res.result.pipeLineVOList
+      })
+    }
+    getPage()
+
+    const handleUsePipeline = (pid) => {
+      usePipelineVisible.value = true
+      usePipelineId.value = pid
+    }
+    const goPipeline = (fileId) => {
+      usePipelineVisible.value = false
+      let data ={
+        pipelineId: usePipelineId.value,
+        fileId: fileId
+      }
+      ElMessageBox( {
+        title: "确定要使用流水线吗?",
+        showCancelButton: true,
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        beforeClose: (action, instance, done) => {
+          if (action === 'confirm') {
+            instance.confirmButtonLoading = true
+            instance.confirmButtonText = '预测中...'
+            usePipeline(data).then(res=>{
+              instance.confirmButtonLoading = false
+              console.log("predic_res: ",res);
+              if (res.msg === '成功')
+              {
+                ElMessageBox({
+                  title: '流水线使用',
+                  message: `流水线使用成功`,
+                  confirmButtonText: '确定',
+                })
+                handleDownload(res.result.predictedFileDownloadingUri)
+                done()
+              }
+              else
+              {
+                console.log(res);
+                ElMessage.error("预测失败!失败码:"+res.msg);
+                done()
+              }
+            }).catch(err=>{
+              console.log(err)
+            });
+          } else {
+            done()
+          }
+        },
+      })
+    }
+
+    const handleDownload=(url)=>{
+      const a = document.createElement('a')
+      a.href = url
+      //a.download = res.success.fileName // 下载后文件名
+      a.style.display = 'none'
+      document.body.appendChild(a)
+      a.click() // 点击下载
+      document.body.removeChild(a)
+    }
+    const handleDelete = (pid) => {
+      ElMessageBox.confirm("确定要删除此流水线吗?", "提示", {
+        type: "warning",
+      }).then(()=>{
+            deletePipeline({pid}).then(res=>{
+              if(res.msg === '成功'){
+                ElMessage.success("删除成功");
+              }
+              else{
+                ElMessage.error("删除失败");
+              }
+            })
+          })
+    }
+
+    return{
+      tableData,
+      usePipelineVisible,
+      useForm,
+      formLabelWidth,
+      fileList,
+      usePipelineId,
+      getPage,
+      handleUsePipeline,
+      handleDelete,
+      goPipeline,
+      handleDownload,
+    }
+  }
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+}
+.green {
+  color: rgb(32, 220, 88)
+}
+.yellow{
+  color:yellowgreen;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 187 - 74
src/views/ModelList.vue → src/views/ml/ModelList.vue

@@ -31,36 +31,44 @@
           @row-dblclick="getConfigDetail"
       >
         <el-table-column prop="modelName" label="模型名"></el-table-column>
-        <el-table-column prop="configName" label="配置名"></el-table-column>
-        <el-table-column label="模型类别">
-          <template #default="scope">{{ scope.row.modelTypeName }}</template>
+        <el-table-column prop="configId" label="所属配置"></el-table-column>
+        <el-table-column label="模型算法">
+          <template #default="scope">{{ scope.row.machineLearningAlgorithm }}</template>
         </el-table-column>
         <el-table-column prop="arguments" label="参数值">
           <template #default="scope">
-            <div v-for="(item,index) in scope.row.arguments" :key="index">{{item}}</div>
+            <div v-for="(item,index) in scope.row.trainParams" :key="index">{{item}}</div>
           </template>
         </el-table-column>
         <el-table-column prop="trainedStatus" label="训练状态">
           <template #default="scope">
-            <div v-if="scope.row.trainedStatus==='已训练'" >
+            <div v-if="scope.row.trainingState==='SUCCESS'" >
               <el-button type="text" icon="el-icon-circle-check" class="yellow"
                          @click="handleTrainRes(scope.$index, scope.row)">查看训练结果</el-button>
+              <br/>
+              <el-button type="text" icon="el-icon-data-board" class="orange"
+                         @click="handlePreList(scope.row.id)">查看预测结果</el-button>
             </div>
             <div v-else>
               <el-button type="text" icon="el-icon-video-play" class="green"
-                       @click="handleTrain(scope.row.modelId)">模型训练</el-button>
+                       @click="handleTrain(scope.row.id)">模型训练</el-button>
+              <br/>
+              <el-button type="text" icon="el-icon-lock" style="color: gray">暂未成功训练</el-button>
             </div>
           </template>
         </el-table-column>
         <el-table-column label="操作" width="180" align="center">
           <template #default="scope">
-            <div v-if="scope.row.trainedStatus==='已训练'">
+            <div v-if="scope.row.trainingState==='SUCCESS'">
               <el-button type="text" icon="el-icon-mouse"
-                         @click="editUseModel(scope.$index, scope.row)">模型使用
+                         @click="editUseModel(scope.row.id)">模型使用
+              </el-button>
+              <el-button type="text" icon="el-icon-stopwatch"
+                         @click="handlePipeline(scope.row.id)">新建流水线
               </el-button>
             </div>
             <div v-else>
-              <el-button type="text" icon="el-icon-lock" style="color: gray">暂未训练</el-button>
+              <el-button type="text" icon="el-icon-lock" style="color: gray">暂未成功训练</el-button>
             </div>
             <el-button type="text" icon="el-icon-delete" class="red"
                        @click="handleDelete(scope.$index, scope.row)">删除</el-button>
@@ -85,9 +93,9 @@
                 <el-select v-model="useform.file" :label-width="formLabelWidth" placeholder="请选择您要使用的文件">
                   <el-option
                     v-for="item in fileList.valueOf()"
-                    :key="item.fileId"
+                    :key="item.id"
                     :label="item.fileName"
-                    :value="item.fileId"
+                    :value="item.id"
                   />
                 </el-select>
               </el-form-item>
@@ -95,16 +103,42 @@
         <template #footer>
           <span class="dialog-footer">
             <el-button @click="useModelVisible = false">取消</el-button>
-            <router-link :to="{path:'/charts',query:{fileId:useform.file,modelId:usemodelId}}">
-            <el-button type="primary" 
-            @click="useModel()"
-              >使用模型</el-button
-            >
-            </router-link>
+            <el-button type="primary" @click="useModel(useform.file)" style="margin-left: 10px"> 使用模型 </el-button>
           </span>
         </template>
       </el-dialog>
 
+    <el-dialog
+        v-model="pipelineVisible"
+        title="新建流水线"
+        width="30%"
+        :before-close="handleClose"
+    >
+      <el-form width="300px">
+        <el-form-item label="流水线名称" required>
+          <el-input v-model="pplForm.pipelineName"></el-input>
+        </el-form-item>
+        <el-form-item label="机器学习算法:" >
+          {{pplForm.machineLearningAlgorithm.algorithmName}}
+        </el-form-item>
+        <el-form-item label="训练参数:" >
+          {{pplForm.trainParams}}
+        </el-form-item>
+        <el-form-item label="特征:" >
+          {{pplForm.features}}
+        </el-form-item>
+        <el-form-item label="标签:" >
+          {{pplForm.label}}
+        </el-form-item>
+      </el-form>
+      <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="pipelineVisible = false">取消</el-button>
+            <el-button type="primary" @click="pipeline()" style="margin-left: 10px"> 新建流水线 </el-button>
+          </span>
+      </template>
+    </el-dialog>
+
   </div>
 
 </template>
@@ -112,26 +146,19 @@
 <script>
 import { ref, reactive } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import {getAllModel,train,use} from "../api/model";
-import {GetAllFile} from "../api/file";
+import {getAllModel, getOneModel, train, use} from "../../api/model";
+import {GetAllFile} from "../../api/file";
 import {useRoute, useRouter} from "vue-router";
-import {deleteModel} from "../api/model";
+import {deleteModel} from "../../api/model";
+import {getOneConfig} from "../../api/config";
+import {addPipeline} from "../../api/pipeline";
 
 export default {
   name: "model",
   setup() {
+    const router = useRouter();
     const useModelVisible = ref(false)
     const formLabelWidth='120px';
-    const featureSty = reactive({
-
-      resource: "小天才",
-
-    });
-    const modelSty = reactive({
-
-      resource: "小天才",
-
-    });
     const query = reactive({
       file: "",
       name: "",
@@ -149,48 +176,98 @@ export default {
     const tableData = ref();
 
     const pageTotal = ref(0);
+    let pipelineVisible = ref(false)
+    const pplForm = reactive({
+      configId: '',
+      pipelineName: '',
+      features: [],
+      label: '',
+      machineLearningAlgorithm: {},
+      trainParams: {},
+    })
+    const handlePipeline = (modelId) => {
+      pipelineVisible.value = true
+      getOneModel({modelId}).then(res =>{
+        pplForm.configId = res.result.configId
+        pplForm.machineLearningAlgorithm = res.result.machineLearningAlgorithm
+        pplForm.trainParams = res.result.trainParams
+      })
+      const cId=pplForm.configId
+      getOneConfig({cId}).then(res =>{
+        pplForm.features = res.result.features
+        pplForm.label = res.result.label
+      })
+    }
+    const pipeline = () => {
+      console.log(pplForm)
+      const payload = {
+        userId: 1,
+        pipelineName: pplForm.pipelineName,
+        features: pplForm.features,
+        label: pplForm.label,
+        machineLearningAlgorithm: pplForm.machineLearningAlgorithm,
+        trainParams: pplForm.trainParams,
+      }
+      if (payload.pipelineName === ""){
+        alert("请填写信息!")
+        return
+      }
+      addPipeline(payload).then(res => {
+        if (res.msg === '成功'){
+          pipelineVisible.value = false
+          ElMessageBox({
+            title: '模型训练',
+            message: `加入流水线成功`,
+            confirmButtonText: '确定',
+          })
+        }
+        else{
+          ElMessage.error("预测失败!失败码:"+res.msg);
+        }
+      })
+    }
     // 获取表格数据
 
     const getpage=()=> {
-      // console.log("getpage");
       const uid=1;
       GetAllFile(uid).then(res=>{
-        fileList.value=res.data;
+        fileList.value=res.result.fileInfoVOList;
       })
       getAllModel({
          userId: uid
       }).then(res => {
-        console.log("res",res);
-        tableData.value = res.data;
+        tableData.value = res.result.modelVOList;
         pageTotal.value=tableData.value.length
         for(let i=0;i<tableData.value.length;i++) {
           const reg=new RegExp("\"","g");
-          tableData.value[i].arguments = ((JSON.stringify(tableData.value[i].arguments))+"")
+          tableData.value[i].trainParams = ((JSON.stringify(tableData.value[i].trainParams))+"")
               .replace(reg,"")
               .replace("{","")
               .replace("}","")
               .split(",");
-          if(tableData.value[i].trainedStatus===1){
-            tableData.value[i].trainedStatus="已训练";
-          }else{
-            tableData.value[i].trainedStatus="未训练";
-          }
         }
         const route=useRoute()
-
       })
     };
     getpage();
     // 查询操作
     const handleSearch = () => {
       query.pageIndex = 1;
-      getData();
+      getpage();
     };
     // 分页导航
     const handlePageChange = (val) => {
       query.pageIndex = val;
-      getData();
+      getpage();
     };
+    const handlePreList=(modelId)=>{
+      router.push({
+        path:'predictionList',
+        query:{
+          modelId:modelId,
+        }
+      })
+    }
     //todo模型训练
     const handleTrain =(modelId) => {
       ElMessageBox( {
@@ -209,14 +286,15 @@ export default {
             }).then(res=>{
               instance.confirmButtonLoading = false
               console.log("train_res: ",res);
-              if (res.status === 200 & res.data === true)
+              if (res.msg === '成功')
               {
-                // ElMessageBox({
-                //   title: '模型训练',
-                //   message: `模型训练成功`,
-                //   confirmButtonText: '确定',
-                // })
-                location.reload();
+                ElMessageBox({
+                  title: '模型训练',
+                  message: `模型训练成功`,
+                  confirmButtonText: '确定',
+                })
+                //location.reload();
+                getpage();
                 done()
               }
               else
@@ -225,7 +303,8 @@ export default {
                 ElMessage.error("训练失败!失败码:"+res.status);
                 done()
               }
-            }).catch(err=>{
+            }
+            ).catch(err=>{
               console.log(err)
             });
           } else {
@@ -238,7 +317,7 @@ export default {
     const resVisible = ref(false);
     const handleTrainRes = (index) => {
       const reg=new RegExp("\"","g");
-     modelRes.value= ((JSON.stringify(tableData.value[index].resOfModel)+"")
+     modelRes.value= ((JSON.stringify(tableData.value[index].trainResultMap)+"")
             .replace(reg,"")
             .replace("{","")
             .replace("}","")
@@ -249,42 +328,69 @@ export default {
         str=str+modelRes.value[i]
         str=str+"\n"
       }
-       ElMessageBox.confirm(tableData.value[index].resOfModel)
-
+       ElMessageBox.confirm(str)
       }
 
-
-
-const usemodelId=ref();
+    const usemodelId=ref();
     //todo模型使用
-     const editUseModel = (index) => {
-      console.log(useModelVisible)
+     const editUseModel = (id) => {
       useModelVisible.value=true;
-      usemodelId.value=tableData.value[index].modelId
+      usemodelId.value=id
     };
 
     let useform = reactive({
       file: "",
     });
         //todo模型使用
-     const useModel = () => {
-      console.log("模型使用")
+     const useModel = (fileId) => {
       useModelVisible.value=false;
-      //console.log(useform.file+" "+usemodelId.value)
-      // use() todo
-       var data={
+       let data={
         modelId:usemodelId.value,
-         fileId:useform.file
+         fileId:fileId
        }
-       console.log(data)
+       ElMessageBox( {
+         title: "确定要预测吗?",
+         showCancelButton: true,
+         confirmButtonText: '确定',
+         cancelButtonText: '取消',
+         beforeClose: (action, instance, done) => {
+           if (action === 'confirm') {
+             instance.confirmButtonLoading = true
+             instance.confirmButtonText = '预测中...'
+             use(data).then(res=>{
+               instance.confirmButtonLoading = false
+               console.log("predic_res: ",res);
+               if (res.msg === '成功')
+               {
+                 ElMessageBox({
+                   title: '模型使用',
+                   message: `模型使用成功`,
+                   confirmButtonText: '确定',
+                 })
+                 //location.reload();
+                 getpage();
+                 done()
+               }
+               else
+               {
+                 console.log(res);
+                 ElMessage.error("预测失败!失败码:"+res.msg);
+                 done()
+               }
+             }).catch(err=>{
+               console.log(err)
+             });
+           } else {
+             done()
+           }
+         },
+       })
        // use(data).then(res=>{
        //   console.log(res.data);
        // })
-       const router=useRouter()
-       const fileId=useform.file
-
-      //ElMessageBox.alert("成功使用模型,快去查看结果吧",{type:"success"})
-    };
+       // const router=useRouter()
+       // const fileId=useform.file
+     };
 
     // 删除操作(单个)
     const handleDelete = (index) => {
@@ -360,6 +466,7 @@ const usemodelId=ref();
     };
 
     return {
+      router,
       usemodelId,
       query,
       tableData,
@@ -370,6 +477,11 @@ const usemodelId=ref();
       editVisible,
       form,
       useform,
+      pipelineVisible,
+      pplForm,
+      handlePipeline,
+      pipeline,
+      handlePreList,
       handleSearch,
       handlePageChange,
       handleTrain,
@@ -379,8 +491,6 @@ const usemodelId=ref();
       handleSelectionChange,
       handleBatchDelete,
       handleEdit,
-      featureSty,
-      modelSty,
       getpage,
       handleTrainRes
     };
@@ -414,6 +524,9 @@ const usemodelId=ref();
 .yellow{
   color:yellowgreen;
 }
+.orange{
+  color: orange;
+}
 .mr10 {
   margin-right: 10px;
 }

+ 2 - 2
src/views/Analysis.vue → src/views/ml/PredictionDetail.vue

@@ -1,10 +1,10 @@
 <template>
-<div>66</div>
+<div>detail</div>
 </template>
 
 <script>
 export default {
-name: "Analysis"
+  name: "PredictionDetail"
 }
 </script>
 

+ 104 - 0
src/views/ml/PredictionList.vue

@@ -0,0 +1,104 @@
+<template>
+<div>
+  <div class="crumbs">
+    <el-breadcrumb separator="/">
+      <el-breadcrumb-item>
+        <i class="el-icon-lx-cascades"></i> 预测结果列表
+      </el-breadcrumb-item>
+    </el-breadcrumb>
+  </div>
+
+  <div class="container">
+    <el-table
+        :data="tableData" border class="table" ref="multipleTable"
+        header-cell-class-name="table-header"
+    >
+      <el-table-column prop="modelId" label="模型id"></el-table-column>
+      <el-table-column prop="createdTime" label="创建时间"></el-table-column>
+      <el-table-column prop="precitionState" label="预测状态"></el-table-column>
+      <el-table-column prop="finishTime" label="完成时间"></el-table-column>
+
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="scope">
+            <el-button type="text" icon="el-icon-download"
+                       @click="handleDownload(scope.row.predictedFileDownloadingUri)">下载
+            </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+
+
+</div>
+</template>
+
+<script>
+import {getPrediction} from "../../api/prediction";
+import {useRoute} from "vue-router";
+import {ref} from "vue";
+
+export default {
+  name: "PredictionList",
+  setup(){
+    const route=useRoute()
+    const tableData = ref([]);
+
+    const getPage = () => {
+      getPrediction({modelId: route.query.modelId}).then(res => {
+        tableData.value=res.result.PredictionVOList;
+      })
+    }
+    getPage();
+
+    const handleDownload=(url)=>{
+      const a = document.createElement('a')
+      a.href = url
+      //a.download = res.success.fileName // 下载后文件名
+      a.style.display = 'none'
+      document.body.appendChild(a)
+      a.click() // 点击下载
+      document.body.removeChild(a)
+    }
+
+
+    return {
+      route,
+      tableData,
+      getPage,
+      handleDownload,
+    };
+  },
+}
+</script>
+
+<style scoped>
+.handle-box {
+  margin-bottom: 20px;
+}
+
+.handle-select {
+  width: 120px;
+}
+
+.handle-input {
+  width: 300px;
+  display: inline-block;
+}
+.table {
+  width: 100%;
+  font-size: 14px;
+}
+.red {
+  color: #ff0000;
+  margin-left: 10px;
+}
+.mr10 {
+  margin-right: 10px;
+}
+.table-td-thumb {
+  display: block;
+  margin: auto;
+  width: 40px;
+  height: 40px;
+}
+</style>

+ 2 - 2
src/views/changeConfig.vue → src/views/ml/changeConfig.vue

@@ -69,8 +69,8 @@
 
 import { reactive, ref } from "vue";
 import { ElMessage } from "element-plus";
-import {changeConfig} from "../api/config";
-import router from "../router";
+import {changeConfig} from "../../api/config";
+import router from "../../router";
 import {useRoute} from "vue-router";
 // import router from "../router";
 export default {