<!-- 异常检测项目批量导入时的类别选择对话框 -->
|
<template>
|
<el-dialog
|
v-model="dialogVisible"
|
:title="title"
|
width="450px"
|
:close-on-click-modal="false"
|
:close-on-press-escape="false"
|
@close="handleCancel"
|
>
|
<div class="anomaly-category-content">
|
<!-- 导入信息 -->
|
<div class="import-info">
|
<el-icon class="info-icon"><InfoFilled /></el-icon>
|
<span>即将导入 <strong>{{ imageCount }}</strong> 张图片</span>
|
</div>
|
|
<!-- 类别选择 -->
|
<div class="category-section">
|
<div class="section-label">
|
<el-checkbox v-model="enableCategorySelection" size="large">
|
为图片添加类别标注
|
</el-checkbox>
|
</div>
|
|
<!-- 类别单选按钮组 -->
|
<el-radio-group
|
v-model="selectedCategoryKey"
|
class="category-radio-group"
|
:disabled="!enableCategorySelection"
|
>
|
<el-radio
|
v-for="category in existingCategories"
|
:key="category.key"
|
:label="category.key"
|
class="category-radio-item"
|
>
|
<div class="category-option">
|
<span
|
class="category-color-dot"
|
:style="{ backgroundColor: category.color }"
|
></span>
|
<span class="category-title">{{ category.title }}</span>
|
<span class="category-hint" v-if="category.key === 0">
|
(正常样本)
|
</span>
|
</div>
|
</el-radio>
|
</el-radio-group>
|
|
<!-- 类别说明 -->
|
<div class="category-tips" v-if="enableCategorySelection">
|
<el-alert
|
:title="getCategoryTips()"
|
type="info"
|
:closable="false"
|
show-icon
|
/>
|
</div>
|
|
<!-- 不选择类别的说明 -->
|
<div class="no-category-tips" v-else>
|
<el-alert
|
title="直接导入图片,不添加任何标注"
|
type="warning"
|
:closable="false"
|
show-icon
|
/>
|
</div>
|
</div>
|
</div>
|
|
<!-- 底部按钮 -->
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="handleCancel">取消</el-button>
|
<el-button type="primary" @click="handleConfirm">
|
确定导入
|
</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { ref, computed, watch } from 'vue';
|
import { ElMessage } from 'element-plus';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
|
// Props
|
const props = defineProps({
|
visible: {
|
type: Boolean,
|
default: false
|
},
|
imageCount: {
|
type: Number,
|
default: 0
|
},
|
existingCategories: {
|
type: Array,
|
default: () => []
|
},
|
title: {
|
type: String,
|
default: '批量导入异常检测图片'
|
},
|
projectType: {
|
type: String,
|
default: 'anomaly' // 'anomaly' 或 'defect'
|
}
|
});
|
|
// Emits
|
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
|
|
// 响应式数据
|
const dialogVisible = computed({
|
get: () => props.visible,
|
set: (val) => emit('update:visible', val)
|
});
|
|
const selectedCategoryKey = ref('');
|
const enableCategorySelection = ref(false);
|
|
// 获取类别提示信息
|
const getCategoryTips = () => {
|
if (selectedCategoryKey.value === 0) {
|
return props.projectType === 'defect'
|
? '选择 "good" 表示这些是正常样本,用于生成缺陷的基础'
|
: '选择 "good" 表示这些是正常样本,用于训练正常模式';
|
} else if (selectedCategoryKey.value !== null && selectedCategoryKey.value !== '') {
|
return props.projectType === 'defect'
|
? `选择 "${getSelectedCategoryTitle()}" 表示这些图片包含该类型的缺陷`
|
: `选择 "${getSelectedCategoryTitle()}" 表示这些图片包含该类型的异常`;
|
}
|
return '请选择要导入图片的类别';
|
};
|
|
// 获取选中类别的标题
|
const getSelectedCategoryTitle = () => {
|
const category = props.existingCategories.find(c => c.key === selectedCategoryKey.value);
|
return category ? category.title : '';
|
};
|
|
// 方法
|
const handleConfirm = () => {
|
console.log('[AnomalyCategoryDialog] ========== handleConfirm 开始 ==========');
|
console.log('[AnomalyCategoryDialog] 启用类别选择:', enableCategorySelection.value);
|
console.log('[AnomalyCategoryDialog] 选中的类别key:', selectedCategoryKey.value);
|
console.log('[AnomalyCategoryDialog] 待处理图片数量:', props.imageCount);
|
console.log('[AnomalyCategoryDialog] 项目类型:', props.projectType);
|
console.log('[AnomalyCategoryDialog] 现有类别:', props.existingCategories);
|
|
// 如果启用了类别选择
|
if (enableCategorySelection.value) {
|
if (selectedCategoryKey.value === '' || selectedCategoryKey.value === null || selectedCategoryKey.value === undefined) {
|
console.warn('[AnomalyCategoryDialog] 未选择类别,提示用户');
|
ElMessage.warning('请选择一个类别');
|
return;
|
}
|
|
const selectedCategory = props.existingCategories.find(
|
c => c.key === selectedCategoryKey.value
|
);
|
|
if (!selectedCategory) {
|
console.error('[AnomalyCategoryDialog] 选择的类别不存在,key:', selectedCategoryKey.value);
|
ElMessage.error('选择的类别不存在');
|
return;
|
}
|
|
console.log('[AnomalyCategoryDialog] 找到选中的类别:', selectedCategory);
|
const result = {
|
isNew: false,
|
key: selectedCategory.key,
|
title: selectedCategory.title,
|
color: selectedCategory.color,
|
skipAnnotation: false
|
};
|
console.log('[AnomalyCategoryDialog] 发送confirm事件,结果:', result);
|
|
emit('confirm', result);
|
} else {
|
// 不选择类别,直接导入
|
console.log('[AnomalyCategoryDialog] 不选择类别,直接导入');
|
const result = {
|
skipAnnotation: true
|
};
|
console.log('[AnomalyCategoryDialog] 发送confirm事件,结果:', result);
|
|
emit('confirm', result);
|
}
|
|
console.log('[AnomalyCategoryDialog] 重置表单');
|
resetForm();
|
};
|
|
const handleCancel = () => {
|
console.log('[AnomalyCategoryDialog] 用户取消操作');
|
emit('cancel');
|
resetForm();
|
};
|
|
const resetForm = () => {
|
selectedCategoryKey.value = '';
|
enableCategorySelection.value = false;
|
};
|
|
// 监听对话框打开
|
watch(() => props.visible, (newVal) => {
|
if (newVal) {
|
console.log('[AnomalyCategoryDialog] 对话框打开');
|
console.log('[AnomalyCategoryDialog] 图片数量:', props.imageCount);
|
console.log('[AnomalyCategoryDialog] 可用类别:', props.existingCategories);
|
// 重置表单
|
resetForm();
|
}
|
});
|
|
// 监听类别选择开关
|
watch(enableCategorySelection, (newVal) => {
|
console.log('[AnomalyCategoryDialog] 类别选择开关变化:', newVal);
|
if (newVal) {
|
// 当启用类别选择时,设置默认选中
|
// 如果只有一个类别,自动选中
|
if (props.existingCategories.length === 1) {
|
selectedCategoryKey.value = props.existingCategories[0].key;
|
console.log('[AnomalyCategoryDialog] 只有一个类别,自动选中:', selectedCategoryKey.value);
|
}
|
// 如果有 "good" 类别(key为0),默认选中它
|
else if (props.existingCategories.some(c => c.key === 0)) {
|
selectedCategoryKey.value = 0;
|
console.log('[AnomalyCategoryDialog] 默认选中good类别, key: 0');
|
}
|
} else {
|
// 当禁用类别选择时,清空选择
|
selectedCategoryKey.value = '';
|
console.log('[AnomalyCategoryDialog] 禁用类别选择,清空选择');
|
}
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.anomaly-category-content {
|
.import-info {
|
display: flex;
|
align-items: center;
|
padding: 12px;
|
background: #f0f9ff;
|
border-radius: 6px;
|
margin-bottom: 20px;
|
color: #409eff;
|
|
.info-icon {
|
margin-right: 8px;
|
font-size: 18px;
|
}
|
|
strong {
|
font-weight: 600;
|
color: #409eff;
|
}
|
}
|
|
.category-section {
|
.section-label {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 16px;
|
font-weight: 500;
|
}
|
|
.category-radio-group {
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
margin-bottom: 20px;
|
|
.category-radio-item {
|
width: 100%;
|
margin: 0;
|
|
:deep(.el-radio__input) {
|
vertical-align: top;
|
margin-top: 3px;
|
}
|
|
:deep(.el-radio__label) {
|
width: calc(100% - 20px);
|
padding: 0;
|
}
|
|
.category-option {
|
display: flex;
|
align-items: center;
|
padding: 10px 12px;
|
background: #f5f7fa;
|
border-radius: 6px;
|
transition: all 0.2s;
|
width: 100%;
|
|
&:hover {
|
background: #e6f1ff;
|
}
|
|
.category-color-dot {
|
width: 16px;
|
height: 16px;
|
border-radius: 4px;
|
margin-right: 10px;
|
flex-shrink: 0;
|
}
|
|
.category-title {
|
font-size: 14px;
|
font-weight: 500;
|
color: #303133;
|
text-align: left;
|
}
|
|
.category-hint {
|
font-size: 12px;
|
color: #909399;
|
margin-left: 8px;
|
}
|
}
|
}
|
|
:deep(.el-radio.is-checked) {
|
.category-option {
|
background: #e6f1ff;
|
border: 1px solid #409eff;
|
|
.category-title {
|
color: #409eff;
|
}
|
}
|
}
|
}
|
|
.category-tips,
|
.no-category-tips {
|
:deep(.el-alert) {
|
padding: 8px 12px;
|
|
.el-alert__title {
|
font-size: 13px;
|
line-height: 1.4;
|
}
|
}
|
}
|
|
.category-radio-group:disabled {
|
opacity: 0.5;
|
pointer-events: none;
|
}
|
}
|
}
|
|
.dialog-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
}
|
</style>
|