Quellcode durchsuchen

feat: 数据分布饼状图

icenaked vor 3 Jahren
Ursprung
Commit
0c18660934

+ 14 - 4
package-lock.json

@@ -233,12 +233,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>

+ 0 - 9
src/router/index.js

@@ -42,15 +42,6 @@ const routes = [
             //     component: () => import ( /* webpackChunkName: "dashboard" */ "../views/DLModelList.vue")
             // },
 
-            {
-                path: "/modelDetail",
-                name: "modelDetail",
-                meta: {
-                    title: '模型训练'
-                },
-                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/ml/ModelDetail.vue")
-            },
-
             {
                 path: "/fileDetail",
                 name: "fileDetail",

+ 119 - 6
src/views/analysis/AnalysisHistory.vue

@@ -17,23 +17,49 @@
       <el-table-column label="分析方法">
         <template #default="scope">{{ scope.row.analysisFunction }}</template>
       </el-table-column>
-      <el-table-column prop="result" label="结果"></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" >可视化
+            <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()" width="700px">
+    <el-form :model="chart" width="700px">
+      <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: 400px;height: 300px" ></div>
+      </el-form-item>
+
+  </el-form>
+  </el-dialog>
 </div>
 </template>
 
 <script>
 import {useRoute} from "vue-router";
-import {getFileDetail} from "../../api/file";
-import {ref} from "vue";
+import {inject, nextTick, reactive, ref} from "vue";
 import {getAnalysisByConfig} from "../../api/analysis";
 
 export default {
@@ -41,7 +67,13 @@ export default {
   setup(){
     const tableData = ref([]);
     const configId = ref();
-
+    let visualizeVisible = ref(false)
+    const chart = reactive({
+      analysisFunction: '',
+      analysisResult: {},
+      pieResult: {},
+    })
+    const echarts = inject('echarts')
 
     const getData = () => {
       const route=useRoute()
@@ -49,7 +81,7 @@ export default {
         getAnalysisByConfig(route.query.configId).then(res => {
           tableData.value = res.result.visualizationVOList;
           for (let i=0;i<tableData.value.length;i++){
-            tableData.value[i].result=JSON.stringify(tableData.value[i].analysisResult)
+            tableData.value[i].analysisResult=JSON.stringify(tableData.value[i].analysisResult)
           }
           configId.value=route.query.configId
         })
@@ -57,9 +89,90 @@ export default {
     };
     getData();
 
+    const handleVisualize = (index) => {
+      chart.analysisFunction = tableData.value[index].analysisFunction
+      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
+        console.log(chart.pieResult)
+      }
+
+    }
+
+    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 open = () =>{
+      nextTick(() => {
+        //  执行echarts方法
+        drawChart()
+      })
+    }
+
     return{
       tableData,
+      visualizeVisible,
+      chart,
+      echarts,
       getData,
+      handleVisualize,
+      drawChart,
+      open,
     };
   }
 }

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

@@ -58,7 +58,6 @@
 
 <script>
 import {reactive, ref} from "vue";
-import {deletePipeline, getAllPipeline, usePipeline} from "../../api/pipeline";
 import {deleteAnalysisPipeline, getAllAnalysisPipeline, useAnalysisPipeline} from "../../api/analysisPipeline";
 import {ElMessage, ElMessageBox} from "element-plus";
 import {GetAllFile} from "../../api/file";

+ 0 - 373
src/views/ml/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>

+ 0 - 2
src/views/ml/ModelList.vue

@@ -103,9 +103,7 @@
         <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(useform.file)" style="margin-left: 10px"> 使用模型 </el-button>
-<!--            </router-link>-->
           </span>
         </template>
       </el-dialog>