JavaProjectRepo/business-css/frontend/src/views/business/algorithmManagement/modelTrainTask/index.vue
2026-03-12 18:17:40 +08:00

706 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

queryParamsindex<script lang="ts">
export default {
name: "模型训练",
};
</script>
<script setup lang="ts">
import { onMounted, ref, nextTick,reactive } from "vue";
import { ElForm, ElMessage, ElMessageBox,FormRules } from "element-plus";
import { addAlgorithms,deleteAlgorithms,trainAlgorithmsPage,trainPublish} from "@/api/algorithml";
import { searchAlgorithmsPage,algorithmsType } from "@/api/business/algorithm";
import { getDictItemById } from '@/api/dict';
import Page from '@/components/Pagination/page.vue'
const apiUrl = import.meta.env.VITE_APP_BASE_API; // 基础路径
const fileList:any = ref([])
const isSwitch = ref(false);
const textarea = ref('')
// 搜索框
const queryParams:any = ref({
current: 1,
size: 10,
total: 0,
title: '',
algorithmType: '',
deviceType: '',
states: '',
});
//新建训练任务
const rules = reactive<FormRules>({
taskName: [{ required: true, message: "请输入训练任务名称", trigger: "blur" }],
algorithmType: [{ required: true, message: "请选择算法类型", trigger: "change" }],
deviceType: [{ required: true, message: "请选择算法类型", trigger: "blur" }],
});
//分页 总条数
const total = ref()
//定义表格数据
const tableData= ref([]);
// 表格加载
const loading = ref(false)
function gettableData() {
const params = {
name: input.value,
current: queryParams.value.current,
size: queryParams.value.size,
title: queryParams.value.title,
deviceType: queryParams.value.deviceType,
algorithmType: queryParams.value.algorithmType,
status: queryParams.value.status,
};
loading.value = true;
trainAlgorithmsPage(params).then((res:any) => {
tableData.value = res.data.records;
queryParams.value.total = res.data.total;
loading.value = false;
})
}
const algorithmData: any = ref([]);
function getAlgorithmList() { // 获取算法列表
let params = {
name: "",
pageNum: 1,
pageSize: 999,
};
loading.value = false;
searchAlgorithmsPage(params).then((result:any) => {
algorithmData.value = result.records;
}).catch((err:any) => {
});
}
function selectAlgorithmType(type:any) { // 选择算法类型
getAlgorithmType(type)
}
const algorithmsTypeData:any =ref([])
function getAlgorithmType(type:any) { // 获取算法类型
algorithmsTypeData.value = []
algorithmsType(type).then((result:any) => {
if(result !=null && result.defaultHyperParams !=null){
algorithmsTypeData.value = JSON.parse(result.defaultHyperParams);
}
}).catch((err:any) => {
});
}
const menuData:any = ref([]);
// 查询字典项
function menuInit() { // 获取算法类型字典项
let params = {
dictId: 'fe2c3418b8998f4e64d56ab46bfe0fed',
size:99,
current:1
}
getDictItemById(params).then((result: any) => {
menuData.value = result.data.records;
}).catch((err: any) => {
});
}
const statusData:any = ref([]);
function statusInit() { // 获取类型字典项
let params = {
dictId: '9b25718a0998f66cada0893121551447',
size:99,
current:1
}
getDictItemById(params).then((result: any) => {
statusData.value = result.data.records;
gettableData()
}).catch((err: any) => {
});
}
const infoForm = ref();
//搜索内容及点击搜索按钮
const input = ref("");
//新建训练任务
const title = ref("");
const info: any = ref({
name: "",
description: "",
version: "",
principle: "",
status:1,
algorithmType: "",
inputParams: "",
outputParams: "",
inferBaseUrl: "",
trainBaseUrl: "",
supportedDeviceTypes: "",
defaultHyperParams: "",
});
const dialogVisibles = ref(false);
const modelVisible= ref(false);
function addClick() {
title.value = "新增训练任务";
info.value = {
name: "",
description: "",
version: "",
principle: "",
status:'',
algorithmType: "",
inputParams: "",
outputParams: "",
inferBaseUrl: "",
trainBaseUrl: "",
supportedDeviceTypes: "",
defaultHyperParams: "",
};
trainParamsData.value = []
algorithmsTypeData.value = []
fileList.value = []
isSwitch.value = false;
dialogVisibles.value = true;
}
//新建训练任务-确认按钮/修改按钮
function confirmClick(formEl: any) {
formEl.validate((valid: any) => {
if(fileList.value.length == 0){
return ElMessage({
type: "error",
message: "请上传数据源类型",
});
}
if (valid) {
if(isSwitch.value == true){
return
}
isSwitch.value = true
const data = new FormData()
if(algorithmsTypeData.value.length != 0){
let template:any = {}
algorithmsTypeData.value.forEach((element:any) => {
template[element.key] = element.default
});
info.value.trainParams = JSON.stringify(template)
}
data.append('task', JSON.stringify(info.value))
if (fileList.value.length > 0) {
data.append('file', fileList.value[0].raw)
}
dialogVisibles.value = false
addAlgorithms(data).then((res: any) => {
dialogVisibles.value = false;
ElMessage({
message: "新增成功",
type: "success",
})
gettableData();
}).catch(() => {
dialogVisibles.value = false;
isSwitch.value = false;
})
}else{
isSwitch.value = false;
}
})
}
//新建角色-取消按钮
function handleClose() {
dialogVisibles.value = false;
dialogViewVisible.value = false;
if (infoForm.value != null) infoForm.value.resetFields();
}
function handleClosed(){
modelVisible.value = false
}
//修改训练任务
function viewClick(row: any) {
title.value = "查看详情";
info.value = JSON.parse(JSON.stringify(row));
trainParamsData.value = []
if(row.trainParams != null && row.trainParams != ""){
let trainParams = JSON.parse(row.trainParams)
for (const key in trainParams) {
if (!Object.hasOwn(trainParams, key)) continue;
const element = trainParams[key];
trainParamsData.value.push({
key:key,
value:element
})
}
}
dialogViewVisible.value = true;
}
//删除训练任务
function delAloneClick(row: any) {
ElMessageBox.confirm("确定删除此训练任务吗?", "删除提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}) .then(() => {
let params = {
id: row.taskId
};
deleteAlgorithms(params).then(() => {
gettableData();
ElMessage({
type: "success",
message: "删除成功",
});
});
})
}
function dateFormat(row: any) {
const daterc = row;
if (daterc != null) {
var date = new Date(daterc);
var year = date.getFullYear();
var month =
date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
var day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
// 拼接
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
}
}
onMounted(() => {
menuInit() // 获取算法类型字典项
statusInit()
getAlgorithmList() // 获取算法列表
gettableData();
});
function changeStatus(row: any) {
// let params = {
// algorithmId: row.algorithmId,
// status: row.status,
// };
// updateAlgorithms(params).then((res) => {
// gettableData();
// ElMessage({
// type: "success",
// message: "更新成功",
// });
// });
}
function releaseClick(row: any) {
title.value = "发布模型";
textarea.value = ''
info.value = JSON.parse(JSON.stringify(row));
modelVisible.value = true;
}
const isEchartsModel = ref(false) // 是否显示echarts模型
function changeShowResult(isShow:boolean){ // 切换显示结果模型
isEchartsModel.value = isShow
}
const dialogViewVisible = ref(false) // 模型详情弹窗
function handlefilechange(file:any){
fileList.value = []
fileList.value.push(file)
}
function handlefileremove(){
fileList.value = []
}
function statusName(code:any){
let name = ''
console.log(statusData.value)
for(let i = 0;i<statusData.value.length;i++){
if(statusData.value[i].itemCode == code){
name = statusData.value[i].dictName
}
}
return name
}
const trainParamsData:any = ref([])
function addTrainParams(){
trainParamsData.value.push({
key:"",
value:" "
})
}
function resetClick(){
queryParams.value = {
current: 1,
size: 10,
total: 0,
title: '',
algorithmType: '',
deviceType: '',
state: '',
}
gettableData()
}
function dellTrainParams(index:any){
trainParamsData.value.splice(index,1)
}
function confirmsClick(){
if(textarea.value == ''){
ElMessage({
type: "warning",
message: "请输入版本号",
});
return
}
let data= {
taskId: info.value.taskId,
versionTag: textarea.value,
}
trainPublish(data).then((res) => {
gettableData();
ElMessage({
type: "success",
message: "发布成功",
});
})
modelVisible.value=false
}
</script>
<template>
<div class="Algorithms-box">
<div class="conductproject-bg-box">
<div
style="display: flex;display: -webkit-flex; justify-content: space-between; -webkit-justify-content: space-between;margin-bottom: 10px">
<div style="display: flex;display: -webkit-flex;">
<el-input v-model="input" placeholder="请输入训练任务名称" @keyup.enter="gettableData" style="width: 200px" clearable />
<el-select style="width: 200px;margin-left: 10px;" clearable v-model="queryParams.algorithmType" placeholder="请选择算法类型" @change="gettableData">
<el-option v-for="item in algorithmData" :key="item.algorithmType" :label="item.algorithmType" :value="item.algorithmType" ></el-option>
</el-select>
<el-select style="width: 200px;margin-left: 10px;" clearable v-model="queryParams.deviceType" placeholder="请选择设备类型" @change="gettableData">
<el-option v-for="item in menuData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" ></el-option>
</el-select>
<el-select style="width: 200px;margin-left: 10px;" clearable v-model="queryParams.status" placeholder="请选择状态" @change="gettableData">
<el-option v-for="item in statusData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" ></el-option>
</el-select>
</div>
<div style="display: flex;display: -webkit-flex;">
<el-button type="primary" style="margin-left: 10px" @click="gettableData">查询</el-button>
<el-button type="primary" @click="resetClick">重置</el-button>
<el-button type="primary" @click="addClick">新建训练任务</el-button>
</div>
</div>
<el-table v-loading="loading" :data="tableData" style="width: 100%; height: calc(100vh - 260px);margin-bottom: 10px;" border
:header-cell-style="{ background: 'rgb(250 250 250)', color: '#383838', height: '50px' }">
<el-table-column type="selection" width="50" align="center" prop="id"></el-table-column>
<el-table-column type="index" label="序号" width="70" align="center" prop="taskId"></el-table-column>
<el-table-column prop="taskName" label="任务名称" min-width="180" ></el-table-column>
<el-table-column prop="algorithmType" label="算法类型" width="160" ></el-table-column>
<el-table-column prop="deviceType" label="设备类型" width="160" ></el-table-column>
<el-table-column prop="status" label="任务状态" width="100">
<template #default="scope">
{{statusName(scope.row.status)}}
</template>
</el-table-column>
<el-table-column prop="updatedAt" label="创建时间" width="200">
<template #default="scope">
{{ dateFormat(scope.row.updatedAt) }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="100" align="center">
<template #default="scope">
<span
style="display: flex;display: -webkit-flex; justify-content: space-around;-webkit-justify-content: space-around; ">
<img src="@/assets/table/view.png" alt="" title="查看详情"
@click="viewClick(scope.row)" style="cursor: pointer; ">
<img src="@/assets/table/release.png" alt="" title="发布模型" v-if="scope.row.status == 'Success'"
@click="releaseClick(scope.row)" style="cursor: pointer; ">
<img v-else src="@/assets/table/release_disabled.png" alt="" title="发布模型">
<img v-if="scope.row.status == 'Success' ||scope.row.status == 'Failed' " src="@/assets/table/del.png" alt="" title="删除"
@click="delAloneClick(scope.row)" style="cursor: pointer; ">
<img v-else src="@/assets/table/del_disabled.png" alt="" title="删除">
</span>
</template>
</el-table-column>
</el-table>
<Page :total="total" v-model:size="queryParams.size" v-model:current="queryParams.current" @pagination="gettableData()" ></Page>
</div>
<el-dialog v-model="dialogVisibles" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleClose" :title="title"
append-to-body width="1145px" height="600px">
<el-form ref="infoForm" :model="info" :rules="rules" label-width="100px"
style="margin-top: 20px;">
<div style="height: calc(100vh - 260px);overflow: auto;">
<el-form-item label="任务名称" prop="taskName" style="width: 100%;" >
<el-input v-model="info.taskName" style="width: 100%" placeholder="输入任务名称"></el-input>
</el-form-item>
<el-form-item label="设备类型" prop="deviceType" style="width: 100%;" >
<el-select v-model="info.deviceType" placeholder="请选择设备类型">
<el-option v-for="item in menuData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" ></el-option>
</el-select>
</el-form-item>
<el-form-item label="算法类型" prop="algorithmType" style="width: 100%;" >
<el-select v-model="info.algorithmType" placeholder="请选择算法类型" @change="selectAlgorithmType">
<el-option v-for="item in algorithmData" :key="item.algorithmType" :label="item.name" :value="item.algorithmType" ></el-option>
</el-select v-model="info.algorithmType">
</el-form-item>
<el-form-item label="数据源类型" style="width: 100%;" >
<el-upload accept=".xlsx" ref="upload" class="upload-demo" action="" :on-change="handlefilechange" :on-remove="handlefileremove" :auto-upload="false" :file-list="fileList" style="width: calc(100% - 70px);position:relative">
<template #trigger>
<el-button type="primary">点击上传</el-button>
</template>
</el-upload>
</el-form-item>
<el-form-item label="参数配置" style="width: 100%;" >
<div>
<span style="font-weight: bold;">输入数据预处理说明:</span>输入数据在训练前已进行标准化处理StandardScaler,因此 rbf_length_scale 等参数对应的是“标准化后的尺度”,不是原始数据的物理单位。
</div>
<el-table v-loading="loading" :data="algorithmsTypeData" style="width: 100%;" border
:header-cell-style="{ background: 'rgb(250 250 250)', color: '#383838', height: '50px' }">
<el-table-column prop="name" label="参数名称" width="180" ></el-table-column>
<el-table-column prop="key" label="参数标识" width="160" ></el-table-column>
<el-table-column prop="description" label="参数说明" min-width="160" ></el-table-column>
<el-table-column prop="status" label="默认值" width="120">
<template #default="scope">
<el-input v-model="scope.row.default" style="width: 100%" placeholder="输入默认值"></el-input>
</template>
</el-table-column>
<el-table-column prop="range" label="建议范围" width="140">
</el-table-column>
</el-table>
<!-- <div >
<el-button type="primary" @click="addTrainParams" style="margin-bottom: 10px;">添加</el-button>
<div style="display: flex;margin-bottom: 10px;" v-for="(item,index) in trainParamsData" :key="index">
<el-input v-model="item.key" style="width: 300px;margin-left: 10px;" placeholder=""></el-input>
<el-input v-model="item.value" style="width: 300px;margin: 0px 10px;" placeholder=""></el-input>
<el-button type="primary" @click="dellTrainParams(index)" text="删除">删除</el-button>
</div>
</div> -->
</el-form-item>
<el-form-item label="特征映射快照" style="width: 100%;" >
<el-input type="textarea" v-model="info.featureMapSnapshot" style="width: 100%" placeholder="输入特征映射快照"></el-input>
</el-form-item>
</div>
<span class="dialog-footer"
style="display: flex;display: -webkit-flex; justify-content: flex-end;-webkit-justify-content: flex-end;">
<el-button @click="handleClose">取 消</el-button>
<el-button type="primary" @click="confirmClick(infoForm)">提交训练</el-button>
</span>
</el-form>
</el-dialog>
<el-dialog v-model="dialogViewVisible" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleClose" :title="'查看详情'"
append-to-body width="1145px" height="600px">
<div style=" width: calc(100%); height: calc(100vh - 200px);">
<div style="display: flex; margin-bottom: 20px; border-bottom: 1px solid #e5e5e5;padding-bottom: 5px;">
<div @click="changeShowResult(false)" class="adddevice_navigation_left" :class="{'adddevice_navigation_activeleft':!isEchartsModel}">基本信息</div>
<div @click="changeShowResult(true)" class="adddevice_navigation_right" :class="{'adddevice_navigation_activeright':isEchartsModel}">/训练结果</div>
</div>
<el-form ref="infoForm" :model="info" :rules="rules" label-width="100px"
style="margin-top: 20px;" v-if="!isEchartsModel">
<el-form-item label="任务ID" style="width: 100%;" >
<el-input v-model="info.taskId" style="width: 100%" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="任务名称" style="width: 100%;" >
<el-input v-model="info.taskName" style="width: 100%" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="算法类型" style="width: 100%;" >
<el-input v-model="info.algorithmType" style="width: 100%" prop="info.deviceType" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="设备类型" style="width: 100%;" >
<el-select v-model="info.deviceType" placeholder="请选择设备类型" :disabled="true">
<el-option v-for="item in menuData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" ></el-option>
</el-select>
</el-form-item>
<el-form-item label="训练参数" style="width: 100%;">
<div v-for="item in trainParamsData" :key="item" style="margin-bottom: 10px;">
<el-input v-model="item.key" style="width: 300px" :disabled="true"></el-input>
<el-input v-model="item.value" style="width: 300px" :disabled="true"></el-input>
</div>
</el-form-item>
<el-form-item label="特征映射快照" style="width: 100%;" >
<el-input type="textarea" v-model="info.featureMapSnapshot" style="width: 100%" placeholder="输入特征映射快照" disabled></el-input>
</el-form-item>
<el-form-item label="创建时间" style="width: 100%;" >
<el-input v-model="info.createdAt" style="width: 100%" :disabled="true" ></el-input>
</el-form-item>
<!-- <el-form-item label="更新时间" style="width: 100%;" >
<el-input v-model="info.updatedAt" style="width: 100%" :disabled="true"></el-input>
</el-form-item> -->
</el-form>
<div v-else style="width: 100%; height: calc(100% - 50px);">
<div v-if="info.status == 'Success'">
<img :src="apiUrl +'/models/'+ info.metricsImagePath" alt="" style="width: 100%; height: 100%;object-fit:contain;">
</div>
<div v-if="info.status == 'Failed'">
失败
</div>
<div v-if="info.status == 'Training'">
训练中
</div>
</div>
</div>
</el-dialog>
<el-dialog v-model="modelVisible" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleClosed" :title="title"
append-to-body width="600px" height="600px">
<el-form ref="infoForm" :model="info" label-width="100px"
style="margin-top: 20px;">
<el-form-item label="任务名称" prop="taskName" >
<el-input v-model="info.taskName" style="width: 100%" placeholder="输入任务名称" disabled></el-input>
</el-form-item>
<el-form-item label="算法类型" prop="algorithmType" >
<el-select v-model="info.algorithmType" placeholder="请选择算法类型" style="width: 100%;" disabled>
<el-option v-for="item in algorithmData" :key="item.algorithmType" :label="item.name" :value="item.algorithmType" ></el-option>
</el-select v-model="info.algorithmType">
</el-form-item>
<el-form-item label="设备类型" prop="deviceType" >
<el-select v-model="info.deviceType" placeholder="请选择设备类型" style="width: 100%;" disabled>
<el-option v-for="item in menuData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" ></el-option>
</el-select>
</el-form-item>
<el-form-item label="版本号" >
<el-input v-model="textarea" style="width: 100%" />
</el-form-item>
<el-button type="primary" style="margin-left: 60px;" @click="confirmsClick">确定</el-button>
</el-form>
</el-dialog>
</div>
</template>
<style scoped>
.Algorithms-box {
padding-right: 10px;
}
:deep(.el-tree-node__content) {
font-size: 15px;
font-weight: 500;
width: 100%;
height: 40px;
}
.dialog-footer {
display: block;
margin-top: 20px;
}
.conductproject-bg-box {
padding: 20px;
width: 100%;
height: calc(100vh - 130px);
overflow: auto;
background-color: rgba(255, 255, 255, 1);
border: none;
border-radius: 3px;
box-sizing: border-box;
}
.Algorithms_dialog_tabbox{
display: flex;
border-bottom: 1px solid #e4e7ed;
padding-bottom: 10px;
}
.Algorithms_dialog_tabbox_item{
position: relative;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.Algorithms_dialog_tabbox_item span{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Arial Normal', 'Arial';
font-weight: 400;
font-style: normal;
font-size: 14px;
color: #363636;
}
.Algorithms_dialog_tabbox_item span:hover{
color: #266fff;
}
.Algorithms_dialog_tabbox_active span{
color: #fff !important;
}
</style>
<style>
.el-dialog {
padding: 0 !important;
border-radius: 10px !important;
border: 1px solid #363636 !important;
}
.el-dialog .el-dialog__header{
display: flex;
display: -webkit-flex;
justify-content: flex-start;-webkit-justify-content: flex-start;
align-items: center;-webkit-align-items: center;
padding: 10px 20px;
background-color: #f1f3f8 !important;
font-family: 'Arial Negreta', 'Arial Normal', 'Arial', sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
color: #1B1B1B;
text-align: left;
border-radius: 10px 10px 0 0;
height: 50px;
}
.el-dialog .el-dialog__close{
font-size: 22px;
color: rgb(80, 80, 80);
}
.el-dialog .el-dialog__headerbtn{
display: flex;
align-items: center;
}
.el-dialog .el-dialog__body{
padding: 20px 40px !important;
}
.el-dialog .el-input{
--el-input-inner-height: 38px
}
.Algorithms-box .el-button{
height: 36px;
}
.el-dialog .el-button{
height: 40px;
}
</style>