|
|
<template>
|
|
|
<!-- <el-scrollbar ref="scrollbar" height="800px"> -->
|
|
|
<div class="main-top">
|
|
|
<span style="font-weight: bold; font-size: 18px; color: #3596eb">任务描述</span>
|
|
|
<p style="font-weight: 400; font-size: 12px; color: #e6e6e6">(给出任务清单参考,做哪些数据的统计分析)Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
|
|
|
</div>
|
|
|
<div class="main-but">
|
|
|
<el-button @click="algorithmicKnowledge()">
|
|
|
<img src="../../../assets/images/导入.png" alt="" />
|
|
|
算法知识导入
|
|
|
</el-button>
|
|
|
<el-button @click="taskSubmit">
|
|
|
<img src="../../../assets/images/提交.png" alt="" />
|
|
|
实训任务提交
|
|
|
</el-button>
|
|
|
</div>
|
|
|
<div class="app-main">
|
|
|
|
|
|
<div class="main-left">
|
|
|
<el-scrollbar height="560px" style="padding: 0 10px" min-size="5">
|
|
|
<div>
|
|
|
<div class="left-top">
|
|
|
<span>一、选择指标</span>
|
|
|
</div>
|
|
|
<div class="metrics">
|
|
|
<el-select v-model="input" placeholder="请选择" style="width: 180px" @change="optionData">
|
|
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="metrics-table" style="margin-top: 10px">
|
|
|
<el-table v-loading="loading1" border :data="tableData" style="width: 100%" :header-cell-style="headerCellStyle" @selection-change="handleSelectionChange">
|
|
|
<el-table-column type="selection" label="Date" align="center" :selectable="flag" />
|
|
|
<el-table-column prop="text" label="指标" align="center" />
|
|
|
</el-table>
|
|
|
<el-button @click="taskZB">确定</el-button>
|
|
|
</div>
|
|
|
<div class="left-top" style="margin-top: 50px">
|
|
|
<span>二、数据预处理</span>
|
|
|
</div>
|
|
|
<div class="metrics">
|
|
|
<el-select v-model="input2" @change="optionData2" placeholder="请选择" style="width: 180px">
|
|
|
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="metrics" style="margin-top: 15px">
|
|
|
<el-select v-model="input3" @change="optionData2" placeholder="请选择" style="width: 180px">
|
|
|
<el-option v-for="item in options3" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="left-top" style="margin-top: 50px">
|
|
|
<span>三、模型参数设置</span>
|
|
|
</div>
|
|
|
<div class="metrics-table" style="margin-top: 10px">
|
|
|
<span style="font-weight: 400; font-size: 12px; color: #ffffff">聚类方法:</span>
|
|
|
<div class="metrics" style="margin-top: 10px">
|
|
|
<el-select v-model="input4" placeholder="请选择" style="width: 180px">
|
|
|
<el-option v-for="item in options4" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div style="margin-top: 10px">
|
|
|
<span style="font-weight: 400; font-size: 12px; color: #ffffff">设置簇数K值:</span>
|
|
|
<el-input v-model="input5" style="width: 180px; margin-top: 5px" />
|
|
|
</div>
|
|
|
<div style="margin-top: 10px">
|
|
|
<span style="font-weight: 400; font-size: 12px; color: #ffffff">最大迭代次数:</span>
|
|
|
<el-input v-model="input6" style="width: 180px; margin-top: 5px" />
|
|
|
</div>
|
|
|
<el-button @click="clusterAnalysisCalculation">计算</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
<div style="margin-top: auto">
|
|
|
<div class="startOver" @click="startOver()">
|
|
|
<img src="../../../assets/images/重新开始.png" alt="" />
|
|
|
<span>重新开始</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="main-right">
|
|
|
<el-scrollbar height="500px" ref="scrollbar" style="padding: 20px">
|
|
|
<div>
|
|
|
<span style="font-weight: bold; font-size: 18px; color: #3596eb">分析数据:</span>
|
|
|
<el-table ref="exportTableRef" v-loading="loading2" :data="tableData2" style="width: 100%; margin-top: 10px" :header-cell-style="headerCellStyle" height="350">
|
|
|
<el-table-column v-for="column in tableLabel" :key="column.prop" :prop="column.prop" :label="column.label" align="center" />
|
|
|
</el-table>
|
|
|
<!-- <el-pagination style="margin-top:10px" background layout="prev, pager, next" :total="1000" /> -->
|
|
|
</div>
|
|
|
<div class="analysisResults" style="margin-top: 20px">
|
|
|
<span style="font-weight: bold; font-size: 18px; color: #3596eb; display: block">分析结果:</span>
|
|
|
<el-button @click="upLoad" v-if="showChart">下载</el-button>
|
|
|
<el-table :data="tableData3" v-loading="loading3" row-key="TSuid" style="width: 100%; margin-top: 10px" :header-cell-style="headerCellStyle" height="350">
|
|
|
<el-table-column v-for="column in tableLabel" :key="column.prop" :prop="column.prop" :label="column.label" align="center" />
|
|
|
</el-table>
|
|
|
<!-- <el-pagination style="margin-top:10px" background layout="prev, pager, next" :total="1000" /> -->
|
|
|
</div>
|
|
|
<div class="pdfRef" id="pdfRef" style="margin-top: 20px; gap: 25px; display: flex" v-show="showChart">
|
|
|
<!-- <img src="../../../assets/images/图1.png" alt=""> -->
|
|
|
<div style="background: #ffffff">
|
|
|
<div id="main" style="width: 500px; height: 500px"></div>
|
|
|
</div>
|
|
|
|
|
|
<div style="background: #ffffff">
|
|
|
<div id="main2" style="width: 500px; height: 500px"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- </el-scrollbar> -->
|
|
|
|
|
|
<el-dialog v-model="dialogVisible" title="实训任务提交" width="600" custom-class="dialogClass" :show-close="false">
|
|
|
<template #header="{ close, titleId, titleClass }">
|
|
|
<div class="my-header">
|
|
|
<span :id="titleId" :class="titleClass">实训任务提交</span>
|
|
|
<img class="gbImg" src="../../../assets/images/关闭.png" @click="close" alt="" />
|
|
|
</div>
|
|
|
<img class="xtImg" src="../../../assets/images/线条.png" alt="" />
|
|
|
</template>
|
|
|
<div class="from-item">
|
|
|
<el-form :inline="true" label-position="right" label-width="115px" :model="form" class="demo-form-inline">
|
|
|
<el-form-item label="聚类个数:">
|
|
|
<el-input style="width: 120px" v-model="formInline2.value1" clearable />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="最大迭代次数:">
|
|
|
<el-input style="width: 120px" v-model="formInline2.value2" clearable />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<span style="color: #ffffff; font-weight: 700">各簇类特征描述:</span>
|
|
|
<el-form :inline="true" label-position="right" style="margin: 15px 0" label-width="115px" :model="form" class="demo-form-inline">
|
|
|
<el-form-item label="簇类1:">
|
|
|
<el-input style="width: 240px" v-model="formInline2.value3" clearable />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="簇类2:">
|
|
|
<el-input style="width: 240px" v-model="formInline2.value4" clearable />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="簇类3:">
|
|
|
<el-input style="width: 240px" v-model="formInline2.value5" clearable />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="簇类4:">
|
|
|
<el-input style="width: 240px" v-model="formInline2.value6" clearable />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
</div>
|
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
|
<el-button @click="task()">确定</el-button>
|
|
|
<el-button type="primary" @click="dialogVisible = false">返回</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<pop-model :showModel="runResultShow" title="聚类分析" @closePop="runResultShow = false">
|
|
|
<template v-slot:content>
|
|
|
<div>
|
|
|
<p style="font-weight: 700; font-size: 22px">聚类分析的定义及原理</p>
|
|
|
<p style="font-size: 19px">1.定义</p>
|
|
|
<p>
|
|
|
所谓物以类聚、人以群分。聚类分析,即是基于研究对象的特征,将他们分门别类,以让同类别的个体之间差异相对小、相似度相对大,不同类别之间的个体差异大、相似度小。
|
|
|
<br />
|
|
|
聚类分析是一种探索性分析方法,与判别分析不同,聚类分析事先并不知道分类的标准,甚至不知道应该分成几类,而是会根据样本数据的特征,自动进行分类。
|
|
|
</p>
|
|
|
<img src="@/assets/images/f1001.png" />
|
|
|
<p>聚类与分类的不同在于,聚类所要求划分的类是未知的</p>
|
|
|
<img src="@/assets/images/f1002.png" />
|
|
|
<img src="@/assets/images/f1003.png" />
|
|
|
<img src="@/assets/images/f1005.png" />
|
|
|
<p style="font-size: 19px">2.原理</p>
|
|
|
<p>
|
|
|
假定研究对象均用所谓的“点”来表示。
|
|
|
<br />
|
|
|
在聚类分析中,一般的规则是将“距离”较小的点归为同一类,将“距离”较大的点归为不同的类。常见的是对个案分类,也可以对变量分类,但对于变量分类此时一般使用相似系数作为“距离”测量指标。
|
|
|
<br />
|
|
|
一般的规则:
|
|
|
</p>
|
|
|
<img src="@/assets/images/f10010.png" />
|
|
|
<br />
|
|
|
<img src="@/assets/images/f10011.png" />
|
|
|
<br />
|
|
|
<img src="@/assets/images/f10012.png" />
|
|
|
<p style="font-weight: 700; font-size: 22px">聚类方法及其在SPSS中的实现</p>
|
|
|
<p style="font-size: 19px">1.主要的聚类方法:</p>
|
|
|
<img src="@/assets/images/f10013.png" />
|
|
|
<p style="font-size: 19px">2.方法详解:</p>
|
|
|
<p style="font-size: 17px">(1):K-means聚类</p>
|
|
|
<p>
|
|
|
又称为快速聚类(K-Means Cluster),是在聚类的类别数已确定的情况下,快速将其他个案归类到相应的类别,适合大样本数据的聚类。
|
|
|
<br />
|
|
|
具体步骤如下:
|
|
|
</p>
|
|
|
<img src="@/assets/images/f10014.png" />
|
|
|
<img src="@/assets/images/f10015.png" />
|
|
|
<p>距离计算规则(欧几里得距离公式):</p>
|
|
|
<img src="@/assets/images/f10016.png" />
|
|
|
<p>图解:</p>
|
|
|
<img src="@/assets/images/f10017.png" />
|
|
|
<img src="@/assets/images/f10018.png" />
|
|
|
<p>
|
|
|
K-means的优缺点:
|
|
|
<br />
|
|
|
优势:
|
|
|
<br />
|
|
|
(1)原理比较简单,实现也很容易,收敛速度快。
|
|
|
<br />
|
|
|
(2)在对大规模数据集进行聚类分析时,算法聚类较高效且聚类效果较好。
|
|
|
<br />
|
|
|
(3)簇与簇之间区别明显时,它的聚类效果很好。
|
|
|
<br />
|
|
|
不足:
|
|
|
<br />
|
|
|
(1)分类数从初始分类开始就确定不变了,所以要求事先要对样本有足够的了解。
|
|
|
<br />
|
|
|
(2)仅限于个案间的聚类(Q型聚类),不能对变量进行聚类。
|
|
|
<br />
|
|
|
(3)个案间的距离的测量方法使用的是欧式距离的平方,因此只能对连续变量进行聚类。
|
|
|
<br />
|
|
|
案例分析(SPSS):
|
|
|
<br />
|
|
|
通过查询整理出了2018年我国各省份的20项基本情况,根据这些指标把这31个省市或地区分成3类。
|
|
|
</p>
|
|
|
<img src="@/assets/images/f10019.png" />
|
|
|
<p>分析步骤:分析>>分类>>K-均值聚类>>迭代>>次数>>选项>>勾选统计>>确认</p>
|
|
|
<img src="@/assets/images/f10020.png" />
|
|
|
<img src="@/assets/images/f10021.png" />
|
|
|
<p>结果分析:</p>
|
|
|
<img src="@/assets/images/f10022.png" />
|
|
|
<img src="@/assets/images/f10023.png" />
|
|
|
<p>若不收敛则调大迭代次数</p>
|
|
|
<img src="@/assets/images/f10024.png" />
|
|
|
<p>方差分析表:</p>
|
|
|
<img src="@/assets/images/f10025.png" />
|
|
|
<p>其中聚类均方对应组间均方差,误差均方对应组内均方差,显著性p<0.05时说明此变量分类效果好。由表可知,大部分变量的p<0.05,且组间均方差大于组内均方差,说明各变量在三个类别中的差异大,分类结果可信度高。</p>
|
|
|
<img src="@/assets/images/f10026.png" />
|
|
|
<br />
|
|
|
<img src="@/assets/images/10027.png" />
|
|
|
<p>聚类结果解读:</p>
|
|
|
<div class="table-box">
|
|
|
<table cellspacing="0" style="width: 355pt">
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td style="text-align: center; vertical-align: middle; width: 355pt">
|
|
|
<p style="margin-left: 0in; text-align: center">
|
|
|
<strong>第一类:</strong>
|
|
|
<strong>1</strong>
|
|
|
<strong>个省份(广东省,经济发达)</strong>
|
|
|
</p>
|
|
|
</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td style="background-color: #e7e7e7; text-align: center; vertical-align: middle; width: 355pt">
|
|
|
<p style="margin-left: 0in; text-align: center">
|
|
|
<strong>第二类:</strong>
|
|
|
<strong>19</strong>
|
|
|
<strong>个省份(云南、西藏等地区,经济一般)</strong>
|
|
|
</p>
|
|
|
</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td style="text-align: center; vertical-align: middle; width: 355pt">
|
|
|
<p style="margin-left: 0in; text-align: center">
|
|
|
<strong>第三类:</strong>
|
|
|
<strong>11</strong>
|
|
|
<strong>个省份(上海、北京等地区,经济较发达)</strong>
|
|
|
</p>
|
|
|
</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
</pop-model>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import * as portraitModel from "@/api/portraitModel";
|
|
|
import popModel from "@/views/components/popModal.vue";
|
|
|
import { htmlPdf } from "@/utils/pdf.js";
|
|
|
import * as echarts from "echarts";
|
|
|
import * as XLSX from "XLSX";
|
|
|
import JSZip from "jszip";
|
|
|
import * as API from "@/api/AI.js";
|
|
|
import { getUserInfo } from "@/utils/auth";
|
|
|
import { onMounted, reactive } from "vue";
|
|
|
const loading1=ref(false)
|
|
|
const loading2=ref(false)
|
|
|
const loading3=ref(false)
|
|
|
const runResultShow = ref(false);
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
const dialogVisible = ref(false);
|
|
|
const tableLabel = reactive([{ prop: "date", label: "" }]);
|
|
|
const isFlag = ref(false);
|
|
|
const formInline2 = reactive({
|
|
|
value1: "",
|
|
|
value2: "",
|
|
|
value3: "",
|
|
|
value4: "",
|
|
|
value5: "",
|
|
|
value6: "",
|
|
|
});
|
|
|
const task = () => {
|
|
|
for (let key in formInline2) {
|
|
|
if (formInline2[key] == "") {
|
|
|
proxy.$modal.msgError("请填写完整!");
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
portraitModel
|
|
|
.submit({
|
|
|
userId: JSON.parse(getUserInfo()).userId,
|
|
|
taskName: "聚类分析",
|
|
|
numberOfErrors: 0,
|
|
|
})
|
|
|
.then((res) => {
|
|
|
dialogVisible.value = false;
|
|
|
proxy.$modal.msgSuccess("提交成功");
|
|
|
});
|
|
|
};
|
|
|
const algorithmicKnowledge = () => {
|
|
|
runResultShow.value = true;
|
|
|
};
|
|
|
const getList = () => {
|
|
|
API.selectionMetrics({ userId: JSON.parse(getUserInfo()).userId }).then((res) => {
|
|
|
res.data.forEach((element) => {
|
|
|
options.value.push({
|
|
|
value: element,
|
|
|
label: element,
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
};
|
|
|
const scrollbar = ref(null);
|
|
|
const fileList = ref([]);
|
|
|
const handleChange = (info) => {
|
|
|
fileList.value.push(info);
|
|
|
};
|
|
|
const flag = ref(false);
|
|
|
let myChart;
|
|
|
let myChart2;
|
|
|
const getRandomColorHex = () => {
|
|
|
const randomValue = (min, max) => {
|
|
|
// 生成 min 到 max 之间的随机整数
|
|
|
const value = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
|
// 将整数转换为两位十六进制形式
|
|
|
return value.toString(16).padStart(2, "0");
|
|
|
};
|
|
|
|
|
|
// 限制红色通道较低,绿色和蓝色通道较高
|
|
|
const r = randomValue(0, 128); // 红色通道较低 (0-128)
|
|
|
const g = randomValue(128, 255); // 绿色通道较高 (128-255)
|
|
|
const b = randomValue(128, 255); // 蓝色通道较高 (128-255)
|
|
|
|
|
|
return `#${r}${g}${b}`;
|
|
|
};
|
|
|
//图标添加数据
|
|
|
const addData = (myChart, newData, color) => {
|
|
|
// 获取当前的数据
|
|
|
let currentData = myChart.getOption().series[0].data || [];
|
|
|
// 添加新数据集,并为每个数据点单独设置颜色
|
|
|
newData.forEach((dataPoint) => {
|
|
|
currentData.push({
|
|
|
value: [dataPoint.x, dataPoint.y],
|
|
|
itemStyle: { color: color },
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// 更新图表
|
|
|
myChart.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
data: currentData,
|
|
|
},
|
|
|
],
|
|
|
});
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
getList();
|
|
|
var chartDom = document.getElementById("main");
|
|
|
var chartDom2 = document.getElementById("main2");
|
|
|
myChart = echarts.init(chartDom);
|
|
|
472;
|
|
|
myChart2 = echarts.init(chartDom2);
|
|
|
// 初始化图表配置
|
|
|
var option = {
|
|
|
dataset: [
|
|
|
{
|
|
|
source: [], // 初始时数据为空
|
|
|
},
|
|
|
],
|
|
|
tooltip: {
|
|
|
position: "top",
|
|
|
},
|
|
|
xAxis: {},
|
|
|
yAxis: {},
|
|
|
series: [
|
|
|
{
|
|
|
type: "scatter",
|
|
|
encode: { tooltip: [0, 1] },
|
|
|
symbolSize: 8,
|
|
|
itemStyle: {
|
|
|
borderColor: "#555",
|
|
|
},
|
|
|
},
|
|
|
],
|
|
|
};
|
|
|
// 设置初始的配置
|
|
|
myChart.setOption(option);
|
|
|
myChart2.setOption(option);
|
|
|
});
|
|
|
|
|
|
const optionData=()=>{
|
|
|
loading1.value=true
|
|
|
API.viewingMetrics({userId:JSON.parse(getUserInfo()).userId,tableName:input.value,algorithmName:'聚类分析'}).then((res)=>{
|
|
|
tableData.value=[]
|
|
|
input3.value=''
|
|
|
res.data.forEach(element => {
|
|
|
tableData.value.push({
|
|
|
text:element
|
|
|
})
|
|
|
})
|
|
|
loading1.value=false
|
|
|
}).catch(()=>{
|
|
|
loading1.value=false
|
|
|
})
|
|
|
}
|
|
|
const selectedRows = ref([]);
|
|
|
const handleSelectionChange = (selection) => {
|
|
|
selectedRows.value = selection;
|
|
|
};
|
|
|
const tableData2=ref([])
|
|
|
const tableData3=ref([])
|
|
|
const resTable2=ref()
|
|
|
const taskZB=()=>{
|
|
|
flag.value=true
|
|
|
if(selectedRows.value.length===0){
|
|
|
return
|
|
|
}
|
|
|
const sendData={
|
|
|
tableName:input.value,
|
|
|
userId:JSON.parse(getUserInfo()).userId,
|
|
|
fieldList:selectedRows.value.map(item=>item.text)
|
|
|
}
|
|
|
API.analysisDataDisplay(sendData).then((res)=>{
|
|
|
tableLabel.length=0
|
|
|
for (const key in res.data[0]) {
|
|
|
tableLabel.push({
|
|
|
label:key,
|
|
|
prop:key
|
|
|
})
|
|
|
}
|
|
|
tableData2.value=res.data
|
|
|
flag.value=true
|
|
|
proxy.$modal.msgSuccess("计算完成!");
|
|
|
tableData2.value = res.data;
|
|
|
flag.value = true;
|
|
|
});
|
|
|
};
|
|
|
const formInline = reactive({
|
|
|
user: "",
|
|
|
region: "",
|
|
|
});
|
|
|
const input = ref();
|
|
|
const options = ref([]);
|
|
|
const input2 = ref();
|
|
|
const options2 = ref([
|
|
|
{
|
|
|
value: "数据区重",
|
|
|
label: "数据去重",
|
|
|
},
|
|
|
]);
|
|
|
const input3 = ref();
|
|
|
const options3 = ref([
|
|
|
{
|
|
|
value: "缺失值处理--剔除数据",
|
|
|
label: "缺失值处理--剔除数据",
|
|
|
},
|
|
|
{
|
|
|
value: "缺失值处理--均值替换",
|
|
|
label: "缺失值处理--均值替换",
|
|
|
},
|
|
|
]);
|
|
|
const input4 = ref();
|
|
|
const options4 = ref([
|
|
|
{
|
|
|
value: "K-means",
|
|
|
label: "K-means",
|
|
|
},
|
|
|
]);
|
|
|
const input5 = ref();
|
|
|
const input6 = ref();
|
|
|
const taskSubmit = () => {
|
|
|
dialogVisible.value = true;
|
|
|
};
|
|
|
const tableData = ref([]);
|
|
|
const headerCellStyle = () => {
|
|
|
return {
|
|
|
backgroundColor: "#1882DE !important", // 设置表头背景颜色
|
|
|
color: "#ffffff !important",
|
|
|
};
|
|
|
};
|
|
|
const optionData2 = () => {
|
|
|
if (tableData2.value.length === 0) {
|
|
|
input2.value = "";
|
|
|
input3.value = "";
|
|
|
proxy.$modal.msgWarning("请先选择指标!");
|
|
|
return;
|
|
|
}
|
|
|
if (input2.value && input3.value) {
|
|
|
const sendData = ref({
|
|
|
mapList: tableData2.value,
|
|
|
method: input3.value,
|
|
|
userId: JSON.parse(getUserInfo()).userId,
|
|
|
});
|
|
|
loading2.value=true
|
|
|
API.dataPreprocessing(sendData.value).then((res) => {
|
|
|
resTable2.value = res.data;
|
|
|
tableData2.value = [];
|
|
|
tableData2.value = res.data;
|
|
|
proxy.$modal.msgSuccess("预处理成功!");
|
|
|
setTimeout(() => {
|
|
|
loading2.value=false
|
|
|
},500)
|
|
|
}).catch(()=>{
|
|
|
loading2.value=false
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
const showChart = ref(false);
|
|
|
//生产表格
|
|
|
const nowData = ref([]);
|
|
|
const clusterAnalysisCalculation = () => {
|
|
|
if (tableData2.value.length === 0 || input2.value == "" || input3.value == "") {
|
|
|
proxy.$modal.msgWarning("请先计算/对数据进行预处理!");
|
|
|
return;
|
|
|
}
|
|
|
if (input5.value && input6.value && input4.value) {
|
|
|
loading3.value=true
|
|
|
const sendData = {
|
|
|
k: input5.value,
|
|
|
t: input6.value,
|
|
|
userId: JSON.parse(getUserInfo()).userId,
|
|
|
deduplicatedDataList: resTable2.value,
|
|
|
};
|
|
|
API.clusterAnalysisPlot(sendData)
|
|
|
.then((res) => {
|
|
|
showChart.value = true;
|
|
|
let sex = 1;
|
|
|
tableData3.value = [];
|
|
|
nowData.value = res.data;
|
|
|
res.data.forEach((item, index) => {
|
|
|
const newItem = {};
|
|
|
tableLabel.forEach((label, index) => {
|
|
|
if (index === 0) {
|
|
|
newItem[label.prop] = "簇" + sex; // 动态设置字段名
|
|
|
} else {
|
|
|
newItem[label.prop] = "";
|
|
|
}
|
|
|
});
|
|
|
newItem["TSuid"] = sex;
|
|
|
sex++;
|
|
|
tableData3.value.push(newItem);
|
|
|
tableData3.value[index].children = [];
|
|
|
const children = ref([]);
|
|
|
item.forEach((item2, sexx) => {
|
|
|
const newItem2 = {};
|
|
|
tableLabel.forEach((label, index2) => {
|
|
|
newItem2[label.prop] = item2[index2]; // 动态设置字段名
|
|
|
});
|
|
|
newItem2["TSuid"] = "" + sex + sexx;
|
|
|
children.value.push(newItem2);
|
|
|
});
|
|
|
tableData3.value[index].children = children.value;
|
|
|
console.log(tableData3.value);
|
|
|
});
|
|
|
})
|
|
|
.then(() => {
|
|
|
API.clusterAnalysis(sendData).then((res) => {
|
|
|
myChart.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
data: [], // 清空数据
|
|
|
},
|
|
|
],
|
|
|
});
|
|
|
myChart2.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
data: [], // 清空数据
|
|
|
},
|
|
|
],
|
|
|
});
|
|
|
Object.keys(res.data.dataSet).forEach((key) => {
|
|
|
const bgColor = getRandomColorHex();
|
|
|
addData(myChart, res.data.dataSet[key], bgColor);
|
|
|
addData(myChart2, res.data.dataSet[key], bgColor);
|
|
|
});
|
|
|
Object.keys(res.data.centroid).forEach((key) => {
|
|
|
addData(myChart2, res.data.centroid[key], "red");
|
|
|
});
|
|
|
setTimeout(() => {
|
|
|
loading3.value=false
|
|
|
},500)
|
|
|
});
|
|
|
}).catch(()=>{
|
|
|
loading3.value=false
|
|
|
});
|
|
|
nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
const scrollContainer = scrollbar.value?.$el.querySelector(".el-scrollbar__wrap");
|
|
|
if (scrollContainer) {
|
|
|
scrollContainer.scrollTo({
|
|
|
top: scrollContainer.scrollHeight,
|
|
|
behavior: "smooth", // 平滑滚动
|
|
|
});
|
|
|
}
|
|
|
}, 100);
|
|
|
});
|
|
|
} else {
|
|
|
proxy.$modal.msgWarning("请模型参数进行设置!");
|
|
|
}
|
|
|
};
|
|
|
const exportTableRef = ref(null);
|
|
|
const upLoad = () => {
|
|
|
const tableDom = exportTableRef.value?.$el;
|
|
|
if (!tableDom) {
|
|
|
return;
|
|
|
}
|
|
|
const wb = XLSX.utils.table_to_book(tableDom);
|
|
|
// XLSX.writeFile(wb, '聚类分析.xlsx');
|
|
|
const zip = new JSZip();
|
|
|
const excelBlob = XLSX.write(wb, { bookType: "xlsx", type: "array" });
|
|
|
zip.file("聚类分析.xlsx", new Blob([excelBlob], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }));
|
|
|
zip.file("聚类分析图表.pdf", handleExport());
|
|
|
zip.generateAsync({ type: "blob" }).then((content) => {
|
|
|
saveAs(content, "聚类分析.zip");
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const startOver = () => {
|
|
|
myChart.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
data: [], // 清空数据
|
|
|
},
|
|
|
],
|
|
|
});
|
|
|
myChart2.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
data: [], // 清空数据
|
|
|
},
|
|
|
],
|
|
|
});
|
|
|
input.value = "";
|
|
|
tableData.value = [];
|
|
|
tableData2.value = [];
|
|
|
tableData3.value = [];
|
|
|
input2.value = "";
|
|
|
input3.value = "";
|
|
|
input4.value = "";
|
|
|
input5.value = "";
|
|
|
input6.value = "";
|
|
|
showChart.value = false;
|
|
|
tableLabel.length = 0;
|
|
|
nowData.value = [];
|
|
|
};
|
|
|
//图表导出pdf
|
|
|
const handleExport = async (name) => {
|
|
|
var fileName = "聚类分析图表";
|
|
|
const fileList = document.getElementsByClassName("pdfRef"); // 很重要
|
|
|
const pdfBlob = await htmlPdf(fileName, document.querySelector("#pdfRef"), fileList, true);
|
|
|
return pdfBlob;
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.main-top {
|
|
|
margin: 20px 20px 0;
|
|
|
margin-top: 10px;
|
|
|
height: 85px;
|
|
|
background: #001d40;
|
|
|
border: 1px solid #0452c6;
|
|
|
padding: 5px 20px;
|
|
|
}
|
|
|
.app-main {
|
|
|
// background-color: #f5f5f5;
|
|
|
// min-height: 965px;
|
|
|
margin: 0px 20px 0;
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
// overflow: auto;
|
|
|
.main-left {
|
|
|
//水平居中
|
|
|
flex-direction: column;
|
|
|
// min-height: 965px;
|
|
|
display: flex;
|
|
|
width: 205px;
|
|
|
height: 100%;
|
|
|
background: #041c49;
|
|
|
// padding: 0px 10px;
|
|
|
.left-top {
|
|
|
padding: 10px;
|
|
|
span {
|
|
|
font-weight: bold;
|
|
|
font-size: 18px;
|
|
|
color: #3596eb;
|
|
|
}
|
|
|
}
|
|
|
.metrics-table {
|
|
|
.el-button {
|
|
|
width: 149px;
|
|
|
height: 48px;
|
|
|
background: url("../../../assets/images/确定.png");
|
|
|
color: #ffffff;
|
|
|
border-color: #ffffff00;
|
|
|
background-size: 100% 100%;
|
|
|
margin: 15px;
|
|
|
}
|
|
|
.el-input {
|
|
|
--el-input-text-color: #ffffff;
|
|
|
--el-input-bg-color: #002651;
|
|
|
--el-input-border-color: #67b3d6;
|
|
|
}
|
|
|
}
|
|
|
.startOver {
|
|
|
// margin-left: -10px;
|
|
|
width: 205px;
|
|
|
height: 55px;
|
|
|
background: #00f4ff;
|
|
|
//水平居中
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
:deep(.el-table) {
|
|
|
--el-table-row-hover-bg-color: #002651 !important;
|
|
|
--el-table-border-color: #7ac1ff;
|
|
|
.el-table__row {
|
|
|
background-color: #002652 !important;
|
|
|
color: #e6e6e6;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.main-right {
|
|
|
width: 1639px;
|
|
|
// height: 100%;
|
|
|
// padding: 20px;
|
|
|
border: 1px solid #0452c6;
|
|
|
:deep(.el-table__header) {
|
|
|
.el-table__cell {
|
|
|
background: linear-gradient(0deg, #5304be 0%, #9126fe 100%) !important;
|
|
|
}
|
|
|
}
|
|
|
:deep(.el-table) {
|
|
|
--el-table-row-hover-bg-color: #002651 !important;
|
|
|
--el-table-border-color: #002651;
|
|
|
--el-table-bg-color: #ffffff00;
|
|
|
.el-table__row {
|
|
|
background-color: #002652 !important;
|
|
|
color: #e6e6e6;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.metrics {
|
|
|
:deep(.el-input) {
|
|
|
.el-input__wrapper {
|
|
|
background-color: #ffffff00;
|
|
|
background-image: url("../../../assets/images/下拉框.png");
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
--el-input-text-color: #ffffff;
|
|
|
--el-input-border-color: #ffffff00;
|
|
|
--el-input-hover-border-color: #ffffff00;
|
|
|
--el-input-hover-border: #ffffff00;
|
|
|
--el-select-border-color-hover: #ffffff00;
|
|
|
--el-select-input-focus-border-color: #ffffff00;
|
|
|
--el-input-placeholder-color: #ffffff;
|
|
|
.el-input__inner {
|
|
|
color: #ffffff;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.analysisResults {
|
|
|
.el-button {
|
|
|
width: 89px;
|
|
|
height: 31px;
|
|
|
background: url("../../../assets/images/下载.png");
|
|
|
color: black;
|
|
|
border-color: #ffffff00;
|
|
|
// background-size: 100% 100%;
|
|
|
margin: 15px 0px 5px;
|
|
|
}
|
|
|
}
|
|
|
:deep(.el-form-item__label) {
|
|
|
color: #ffffff;
|
|
|
}
|
|
|
.from-item {
|
|
|
padding: 20px;
|
|
|
.el-input {
|
|
|
--el-input-text-color: #ffffff;
|
|
|
--el-input-bg-color: #002651;
|
|
|
--el-input-border-color: #67b3d6;
|
|
|
}
|
|
|
}
|
|
|
.dialog-footer {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
:deep(.el-table__expand-icon) {
|
|
|
color: #9126fe;
|
|
|
.el-icon {
|
|
|
font-size: 16px;
|
|
|
}
|
|
|
}
|
|
|
</style>
|