<template>
<div class="classification-container">
<div class="config-panel">
<el-form class="classification-form" :model="form" label-width="120px">
<!-- Task Selection -->
<el-form-item size="large" label="分类任务">
<el-select
v-model="form.taskSelection"
placeholder="请选择分类任务"
clearable
@change="handleTaskSelectionChange"
>
<el-option label="传统分类" value="传统分类" />
<el-option label="小样本分类" value="小样本分类" />
</el-select>
</el-form-item>
<!-- Traditional Classification Section -->
<template v-if="form.taskSelection === '传统分类'">
<el-form-item size="large" label="分类模型">
<el-select v-model="form.model" placeholder="请选择分类模型">
<el-option
v-for="item in modelList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item size="large" label="图片上传">
<el-upload
class="image-uploader"
action="#"
:auto-upload="false"
:on-change="handleTraditionalUpload"
:file-list="traditionalFileList"
multiple
accept="image/*"
list-type="picture-card"
>
<div class="upload-content">
<i class="el-icon-picture-outline upload-icon"></i>
<div class="upload-text">点击选择文件上传</div>
</div>
</el-upload>
</el-form-item>
<el-form-item class="action-buttons">
<el-button type="primary" size="large" @click="sendFormDataToBackend">
开始分类
</el-button>
<el-button size="large" @click="handleRemoveAll">
取消
</el-button>
</el-form-item>
</template>
<!-- Few-shot Classification Section -->
<template v-if="form.taskSelection === '小样本分类'">
<el-form-item size="large" label="分类模型">
<el-select v-model="form.model" placeholder="请选择分类模型">
<el-option
v-for="item in fewmodelList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item size="large" label="小样本任务">
<el-select
v-model="form.fewShotSelectionArray"
placeholder="请选择小样本任务"
clearable
>
<el-option label="3way-1shot" value="[3,1]" />
<el-option label="3way-5shot" value="[3,5]" />
<el-option label="5way-1shot" value="[5,1]" />
<el-option label="5way-5shot" value="[5,5]" />
</el-select>
</el-form-item>
<el-form-item label="支持集类别">
<div class="category-input">
<el-input
v-model="newSupportCategory"
placeholder="请输入支持集类别"
size="large"
/>
<el-button
type="primary"
size="large"
@click="addSupportCategory"
>
添加
</el-button>
</div>
</el-form-item>
<div class="category-tags">
<el-tag
v-for="(category, index) in form.supportCategories"
:key="index"
closable
@close="removeSupportCategory(index)"
>
{{ category }}
</el-tag>
</div>
<el-form-item size="large" label="支持集图片">
<el-upload
class="image-uploader"
action="#"
:auto-upload="false"
:on-change="(file, fileList) => handleFewShotUpload(file, fileList, 'support')"
:file-list="supportFileList"
multiple
accept="image/*"
list-type="picture-card"
>
<div class="upload-content">
<i class="el-icon-picture-outline upload-icon"></i>
<div class="upload-text">点击选择支持集文件</div>
</div>
</el-upload>
</el-form-item>
<el-form-item size="large" label="查询集图片">
<el-upload
class="image-uploader"
action="#"
:auto-upload="false"
:on-change="(file, fileList) => handleFewShotUpload(file, fileList, 'query')"
:file-list="queryFileList"
multiple
accept="image/*"
list-type="picture-card"
>
<div class="upload-content">
<i class="el-icon-picture-outline upload-icon"></i>
<div class="upload-text">点击选择查询集文件</div>
</div>
</el-upload>
</el-form-item>
<el-form-item class="action-buttons">
<el-button type="primary" size="large" @click="sendFormDataToBackend">
开始分类
</el-button>
<el-button size="large" @click="handleRemoveAll">
取消
</el-button>
</el-form-item>
</template>
</el-form>
</div>
<div class="result-panel">
<el-table
:data="resultData"
border
stripe
style="width: 100%"
height="100%"
:header-cell-style="tableHeaderColor"
>
<el-table-column
type="index"
label="序号"
width="80"
align="center"
/>
<el-table-column
prop="name"
label="图片名称"
align="center"
/>
<el-table-column
label="图片预览"
align="center"
>
<template slot-scope="scope">
<img
:src="scope.row.preview"
alt="图片预览"
class="preview-image"
@click="previewImage(scope.row.preview)"
/>
</template>
</el-table-column>
<el-table-column
prop="prediction"
label="分类结果"
align="center"
/>
</el-table>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
form: {
taskSelection: '',
model: '',
fewShotSelectionArray: '',
supportCategories: [],
images: [],
supportImages: [],
queryImages: []
},
modelList: [
{ label: 'ResNet18', value: 'resnet18' },
{ label: 'ResNet34', value: 'resnet34' },
{ label: 'ResNet50', value: 'resnet50' },
{ label: 'ViT-B/16', value: 'vit_b_16' },
{ label: 'ViT-L/16', value: 'vit_l_16' }
],
fewmodelList: [
{ label: 'CERE', value: 'CERE' }
],
traditionalFileList: [],
supportFileList: [],
queryFileList: [],
resultData: [],
newSupportCategory: ''
}
},
methods: {
/* 修改表头颜色 */
tableHeaderColor({ rowIndex }) {
if (rowIndex === 0) {
return 'background-color: #00A8A0;color: #fff;font-weight: 500;'
}
},
handleTaskSelectionChange(value) {
this.resetForm();
if (value === '传统分类') {
this.form.fewShotSelectionArray = '';
this.form.model = '';
} else if (value === '小样本分类') {
this.form.model = '';
}
},
handleTraditionalUpload(file, fileList) {
this.traditionalFileList = fileList;
this.form.images = fileList.map(f => f.raw);
this.updateResultData();
},
handleFewShotUpload(file, fileList, type) {
if (type === 'support') {
this.supportFileList = fileList;
this.form.supportImages = fileList.map(f => f.raw);
} else {
this.queryFileList = fileList;
this.form.queryImages = fileList.map(f => f.raw);
}
this.updateResultData();
},
updateResultData() {
if (this.form.taskSelection === '传统分类') {
this.resultData = this.traditionalFileList.map(file => ({
name: file.name,
preview: URL.createObjectURL(file.raw),
prediction: ''
}));
} else {
this.resultData = this.queryFileList.map(file => ({
name: file.name,
preview: URL.createObjectURL(file.raw),
prediction: ''
}));
}
},
addSupportCategory() {
const trimmedCategory = this.newSupportCategory.trim();
if (trimmedCategory && !this.form.supportCategories.includes(trimmedCategory)) {
this.form.supportCategories.push(trimmedCategory);
this.newSupportCategory = '';
}
},
removeSupportCategory(index) {
this.form.supportCategories.splice(index, 1);
},
previewImage(imgPath) {
this.$viewerApi({ images: [imgPath] });
},
resetForm() {
this.traditionalFileList = [];
this.supportFileList = [];
this.queryFileList = [];
this.form.images = [];
this.form.supportImages = [];
this.form.queryImages = [];
this.form.supportCategories = [];
this.resultData = [];
},
handleRemoveAll() {
this.resetForm();
},
async sendFormDataToBackend() {
if (!this.form.model) {
this.$message.error('请选择分类模型');
return;
}
if ((this.form.taskSelection === '小样本分类' && this.queryFileList.length === 0) ||
(this.form.taskSelection === '传统分类' && this.traditionalFileList.length === 0)) {
this.$message.error('请上传图片');
return;
}
if (this.form.taskSelection === '小样本分类' && this.form.supportCategories.length === 0) {
this.$message.error('请添加支持集类别');
return;
}
const formData = new FormData();
formData.append('model', this.form.model);
formData.append('task_type', this.form.taskSelection);
if (this.form.taskSelection === '传统分类') {
this.traditionalFileList.forEach((file) => {
formData.append('images', file.raw);
});
} else {
this.queryFileList.forEach((file) => {
formData.append('query_images', file.raw);
});
this.supportFileList.forEach((file) => {
formData.append('support_images', file.raw);
});
if (this.form.fewShotSelectionArray) {
const [way, shot] = JSON.parse(this.form.fewShotSelectionArray);
formData.append('way', way);
formData.append('shot', shot);
}
formData.append('categories', JSON.stringify(this.form.supportCategories));
}
try {
const loading = this.$loading({
lock: true,
text: '正在分类中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const response = await axios.post(
'http://192.168.200.60:5010/classification/classify',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
);
if (response.data) {
this.resultData = this.resultData.map(item => {
const classificationResult = response.data[item.name];
return {
...item,
prediction: classificationResult || '未知'
};
});
}
this.$message.success('分类成功');
loading.close();
} catch (error) {
console.error('分类失败:', error);
let errorMessage = '分类失败';
if (error.response) {
errorMessage += ': ' + (
error.response.data.message ||
error.response.data.error ||
error.response.statusText
);
} else if (error.message) {
errorMessage += ': ' + error.message;
}
this.$message.error(errorMessage);
} finally {
if (this.$loading) {
this.$loading().close();
}
}
}
},
beforeDestroy() {
this.resultData.forEach(item => {
if (item.preview) {
URL.revokeObjectURL(item.preview);
}
});
}
}
</script>
<style lang="scss" scoped>
.classification-container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
height: 100%;
padding: 20px;
box-sizing: border-box;
background-color: #f5f7fa;
}
.config-panel {
padding: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: auto;
}
.result-panel {
padding: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.classification-form {
max-width: 600px;
margin: 0 auto;
.el-select {
width: 100%;
}
}
.category-input {
display: flex;
gap: 10px;
.el-input {
flex: 1;
}
}
.category-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 10px 0 20px 120px;
}
.image-uploader {
::v-deep .el-upload {
width: 100%;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
border: 2px dashed #00A8A0;
border-radius: 8px;
background-color: rgba(0, 168, 160, 0.03);
transition: all 0.3s ease;
&:hover {
border-color: #009688;
background-color: rgba(0, 168, 160, 0.05);
}
}
::v-deep .el-upload-list--picture-card {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
.el-upload-list__item {
width: 100px;
height: 100px;
}
}
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #00A8A0;
.upload-icon {
font-size: 40px;
margin-bottom: 10px;
}
.upload-text {
font-size: 16px;
line-height: 1.5;
}
}
.action-buttons {
display: flex;
justify-content: space-between;
.el-button {
flex: 1;
&:first-child {
margin-right: 10px;
}
}
}
.preview-image {
max-width: 100px;
max-height: 100px;
cursor: pointer;
display: block;
margin: 0 auto;
object-fit: contain;
&:hover {
transform: scale(1.05);
transition: transform 0.2s ease;
}
}
@media (max-width: 992px) {
.classification-container {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.classification-form {
padding: 10px;
}
.image-uploader {
::v-deep .el-upload-list--picture-card .el-upload-list__item {
width: 80px;
height: 80px;
}
}
.category-tags {
margin-left: 0;
}
}
</style>
if (this.form.taskSelection === '传统分类') {
this.traditionalFileList.forEach((file) => {
formData.append('images', file.raw);
});
}有多个图片传入 图片键名相同,后端接收到只有一个图片咋回事