Industrial-image-management.../src/views/construction-operation-plat.../implementation-workflow/data-processing/model-config/index.vue

498 lines
14 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.

<template>
<div class="model-config-container">
<a-card title="模型配置" :bordered="false">
<a-space direction="vertical" fill :size="16">
<!-- 操作栏 -->
<div class="operation-bar">
<a-space>
<a-button type="primary" @click="handleAdd">
<template #icon><icon-plus /></template>
新建配置
</a-button>
<a-button @click="refreshList">
<template #icon><icon-refresh /></template>
刷新
</a-button>
</a-space>
<a-space>
<!-- 搜索栏 -->
<a-input-search
v-model="searchKeyword"
placeholder="请输入模型名称或ID搜索"
search-button
@search="handleSearch"
/>
</a-space>
</div>
<!-- 列表 -->
<a-table
:data="tableData"
:loading="loading"
:pagination="pagination"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
row-key="modelId"
>
<template #columns>
<!-- <a-table-column title="模型ID" data-index="modelId" /> -->
<a-table-column title="模型名称" data-index="modelName" />
<a-table-column title="模型路径" data-index="modelPath" />
<a-table-column title="置信度阈值" data-index="confThreshold">
<template #cell="{ record }">
{{ record.confThreshold ? record.confThreshold.toFixed(2) : '-' }}
</template>
</a-table-column>
<a-table-column title="NMS阈值" data-index="nmsThreshold">
<template #cell="{ record }">
{{ record.nmsThreshold ? record.nmsThreshold.toFixed(2) : '-' }}
</template>
</a-table-column>
<a-table-column title="操作" fixed="right" :width="180">
<template #cell="{ record }">
<a-space>
<a-button type="text" size="small" @click="handleEdit(record)">
<template #icon><icon-edit /></template>
编辑
</a-button>
<a-button type="text" size="small" @click="handleView(record)">
<template #icon><icon-eye /></template>
查看
</a-button>
<a-popconfirm
title="确定要删除此配置吗?"
@ok="handleDelete(record)"
>
<a-button type="text" status="danger" size="small">
<template #icon><icon-delete /></template>
删除
</a-button>
</a-popconfirm>
</a-space>
</template>
</a-table-column>
</template>
</a-table>
</a-space>
</a-card>
<!-- 新增/编辑表单 -->
<a-modal
v-model:visible="formVisible"
:title="isEdit ? '编辑模型配置' : '新增模型配置'"
@cancel="closeForm"
@before-ok="handleSubmit"
unmount-on-close
>
<a-form
ref="formRef"
:model="formData"
label-align="right"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
auto-label-width
>
<!-- <a-form-item
field="modelId"
label="模型ID"
:validate-trigger="['change', 'blur']"
:rules="[{ required: true, message: '请输入模型ID' }]"
>
<a-input
v-model="formData.modelId"
placeholder="请输入模型ID"
:disabled="isEdit"
/>
</a-form-item> -->
<a-form-item
field="modelName"
label="模型名称"
:validate-trigger="['change', 'blur']"
:rules="[{ required: true, message: '请输入模型名称' }]"
>
<a-input
v-model="formData.modelName"
placeholder="请输入模型名称"
/>
</a-form-item>
<a-form-item
field="attachId"
label="模型附件"
>
<a-space>
<a-upload
:custom-request="uploadModelFile"
>
<a-button type="primary">
<template #icon><icon-upload /></template>
上传模型文件
</a-button>
</a-upload>
<a-button
v-if="formData.attachId"
status="danger"
@click="formData.attachId = ''"
>
<template #icon><icon-delete /></template>
清除
</a-button>
</a-space>
<div class="upload-tip" v-if="uploadingFile">
<a-spin /> 上传中...{{ uploadProgress }}%
</div>
<div class="upload-tip success" v-if="uploadedFileName && !uploadingFile">
<icon-check-circle /> 已上传: {{ uploadedFileName }}
</div>
</a-form-item>
<a-form-item
field="confThreshold"
label="置信度阈值"
:validate-trigger="['change', 'blur']"
:rules="[
{ required: true, message: '请输入置信度阈值' },
{
validator: (value, cb) => {
if (value < 0 || value > 1) {
cb('置信度阈值必须在0-1之间');
}
}
}
]"
>
<a-input-number
v-model="formData.confThreshold"
placeholder="请输入置信度阈值"
:min="0"
:max="1"
:precision="2"
:step="0.01"
style="width: 100%;"
/>
</a-form-item>
<a-form-item
field="nmsThreshold"
label="NMS阈值"
:validate-trigger="['change', 'blur']"
:rules="[
{ required: true, message: '请输入NMS阈值' },
{
validator: (value, cb) => {
if (value < 0 || value > 1) {
cb('NMS阈值必须在0-1之间');
}
}
}
]"
>
<a-input-number
v-model="formData.nmsThreshold"
placeholder="请输入NMS阈值"
:min="0"
:max="1"
:precision="2"
:step="0.01"
style="width: 100%;"
/>
</a-form-item>
</a-form>
</a-modal>
<!-- 查看详情 -->
<a-modal
v-model:visible="detailVisible"
title="模型配置详情"
@cancel="closeDetail"
:footer="false"
unmount-on-close
>
<a-descriptions
:data="detailData"
:column="1"
title="基本信息"
:bordered="true"
/>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import {
IconPlus,
IconRefresh,
IconEdit,
IconEye,
IconDelete,
IconUpload,
IconCheckCircle
} from '@arco-design/web-vue/es/icon';
import {
getModelConfigList,
getModelConfigDetail,
createModelConfig,
updateModelConfig,
deleteModelConfig
} from '@/apis/model-config';
import { addAttachment } from '@/apis/attach-info';
import type { ModelConfigRequest, ModelConfigResponse } from '@/apis/model-config/type';
defineOptions({ name: 'ModelConfig' });
// 表格数据和加载状态
const tableData = ref<any[]>([]);
const loading = ref(false);
const searchKeyword = ref('');
// 分页信息
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showTotal: true,
showJumper: true,
});
// 表单相关
const formRef = ref();
const formVisible = ref(false);
const isEdit = ref(false);
const formData = reactive<ModelConfigRequest>({
attachId: '',
confThreshold: 0,
modelId: '',
modelName: '',
nmsThreshold: 0,
});
// 详情相关
const detailVisible = ref(false);
const detailData = ref<{ label: string; value: any }[]>([]);
// 上传文件相关
const uploadingFile = ref(false);
const uploadProgress = ref(0);
const uploadedFileName = ref('');
// 获取模型配置列表
const fetchModelConfigList = async () => {
loading.value = true;
try {
const res = await getModelConfigList({
keyword: searchKeyword.value,
page: pagination.current,
pageSize: pagination.pageSize,
});
if (res.data) {
tableData.value = Array.isArray(res.data) ? res.data : [];
pagination.total = Array.isArray(res.data) ? res.data.length : 0;
} else {
tableData.value = [];
pagination.total = 0;
}
} catch (error) {
console.error('获取模型配置列表失败:', error);
Message.error('获取模型配置列表失败');
} finally {
loading.value = false;
}
};
// 初始化
onMounted(() => {
fetchModelConfigList();
});
// 刷新列表
const refreshList = () => {
fetchModelConfigList();
};
// 搜索
const handleSearch = () => {
pagination.current = 1;
fetchModelConfigList();
};
// 分页事件
const onPageChange = (page: number) => {
pagination.current = page;
fetchModelConfigList();
};
const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize;
fetchModelConfigList();
};
// 打开新增表单
const handleAdd = () => {
isEdit.value = false;
resetForm();
formVisible.value = true;
};
// 打开编辑表单
const handleEdit = async (record: ModelConfigResponse) => {
isEdit.value = true;
resetForm();
try {
const res = await getModelConfigDetail(record.modelId);
if (res.data) {
const modelData = res.data.data;
formData.modelId = modelData.modelId;
formData.modelName = modelData.modelName;
formData.attachId = modelData.attachId;
formData.confThreshold = modelData.confThreshold;
formData.nmsThreshold = modelData.nmsThreshold;
formVisible.value = true;
}
} catch (error) {
console.error('获取详情失败:', error);
Message.error('获取详情失败');
}
};
// 查看详情
const handleView = async (record: ModelConfigResponse) => {
try {
const res = await getModelConfigDetail(record.modelId);
if (res.data) {
const modelData = res.data.data;
detailData.value = [
// { label: '模型ID', value: modelData.modelId },
{ label: '模型名称', value: modelData.modelName },
{ label: '模型路径', value: modelData.modelPath || '-' },
{ label: '模型附件ID', value: modelData.attachId || '-' },
{ label: '置信度阈值', value: modelData.confThreshold ? modelData.confThreshold.toFixed(2) : '-' },
{ label: 'NMS阈值', value: modelData.nmsThreshold ? modelData.nmsThreshold.toFixed(2) : '-' },
];
detailVisible.value = true;
}
} catch (error) {
console.error('获取详情失败:', error);
Message.error('获取详情失败');
}
};
// 删除配置
const handleDelete = async (record: ModelConfigResponse) => {
try {
await deleteModelConfig(record.modelId);
Message.success('删除成功');
fetchModelConfigList();
} catch (error) {
console.error('删除失败:', error);
Message.error('删除失败');
}
};
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return false;
try {
await formRef.value.validate();
const submitData = {
modelId: formData.modelId,
modelName: formData.modelName,
attachId: formData.attachId,
confThreshold: formData.confThreshold,
nmsThreshold: formData.nmsThreshold,
};
if (isEdit.value) {
await updateModelConfig(submitData);
Message.success('更新成功');
} else {
await createModelConfig(submitData);
Message.success('创建成功');
}
closeForm();
fetchModelConfigList();
return true;
} catch (error) {
console.error('提交失败:', error);
Message.error('提交失败: ' + (error as any)?.msg || '未知错误');
return false;
}
};
// 重置表单
const resetForm = () => {
// formData.modelId = '';
formData.modelName = '';
formData.attachId = '';
formData.confThreshold = 0;
formData.nmsThreshold = 0;
};
// 关闭表单
const closeForm = () => {
formVisible.value = false;
resetForm();
};
// 关闭详情
const closeDetail = () => {
detailVisible.value = false;
detailData.value = [];
};
// 上传模型文件
const uploadModelFile = async (options: any) => {
const {onProgress, onError, onSuccess, fileItem, name} = options;
try {
// 创建FormData正确设置文件参数
const uploadFormData = new FormData();
uploadFormData.append(name || 'file', fileItem.file);
// 调用添加附件API按照格式 /attach-info/{businessType}
const res = await addAttachment(uploadFormData);
if (res && res.data) {
// 获取上传后的附件ID并设置到表单
const attachId = res.data;
formData.attachId = attachId;
uploadedFileName.value = fileItem.file.name;
Message.success('模型文件上传成功');
onSuccess(res);
} else {
Message.error('模型文件上传失败');
onError(new Error('上传失败'));
}
} catch (error) {
console.error('上传失败:', error);
Message.error('上传失败: ' + (error as any)?.msg || '未知错误');
onError(error);
} finally {
uploadingFile.value = false;
}
};
</script>
<style scoped lang="scss">
.model-config-container {
.operation-bar {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
.upload-tip {
margin-top: 8px;
color: #86909c;
font-size: 14px;
&.success {
color: #00b42a;
}
}
}
</style>