ForcePilot/web/src/components/modals/BenchmarkUploadModal.vue

231 lines
4.8 KiB
Vue
Raw Normal View History

<template>
<a-modal
v-model:open="visible"
title="上传评估基准"
width="600px"
:confirmLoading="uploading"
@ok="handleUpload"
@cancel="handleCancel"
>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
layout="vertical"
>
<a-form-item label="基准名称" name="name">
<a-input
v-model:value="formState.name"
placeholder="请输入评估基准名称"
/>
</a-form-item>
<a-form-item label="描述" name="description">
<a-textarea
v-model:value="formState.description"
placeholder="请输入评估基准描述(可选)"
:rows="3"
/>
</a-form-item>
<a-form-item label="基准文件" name="file">
<a-upload-dragger
v-model:fileList="fileList"
name="file"
:multiple="false"
accept=".jsonl"
:before-upload="beforeUpload"
@remove="handleRemove"
>
<p class="ant-upload-text">
<FileTextOutlined />
点击或拖拽文件到此区域上传
</p>
<p class="ant-upload-hint">
仅支持 JSONL 格式文件.jsonl
</p>
</a-upload-dragger>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import { ref, reactive, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import { FileTextOutlined } from '@ant-design/icons-vue';
import { evaluationApi } from '@/apis/knowledge_api';
const props = defineProps({
visible: {
type: Boolean,
default: false
},
databaseId: {
type: String,
required: true
}
});
const emit = defineEmits(['update:visible', 'success']);
// 响应式数据
const formRef = ref();
const fileList = ref([]);
const uploading = ref(false);
const formState = reactive({
name: '',
description: '',
file: null
});
// 表单验证规则
const rules = {
name: [
{ required: true, message: '请输入基准名称', trigger: 'blur' },
{ min: 2, max: 100, message: '基准名称长度应在2-100个字符之间', trigger: 'blur' }
],
file: [
{ required: true, message: '请选择基准文件', trigger: 'change' }
]
};
// 双向绑定visible
const visible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
});
// 文件上传前验证
const beforeUpload = (file) => {
// 检查文件类型
if (!file.name.endsWith('.jsonl')) {
message.error('仅支持 JSONL 格式文件');
return false;
}
// 检查文件大小限制为10MB
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过 10MB');
return false;
}
// 读取文件内容验证格式
const reader = new FileReader();
reader.onload = (e) => {
try {
const content = e.target.result;
const lines = content.trim().split('\n');
// 验证至少有一行
if (lines.length === 0) {
message.error('文件不能为空');
return false;
}
// 验证JSON格式
for (let i = 0; i < Math.min(5, lines.length); i++) {
const line = lines[i].trim();
if (line) {
JSON.parse(line);
}
}
formState.file = file;
} catch (error) {
message.error('文件格式错误请检查JSONL格式');
return false;
}
};
reader.readAsText(file);
// 阻止自动上传
return false;
};
// 移除文件
const handleRemove = () => {
formState.file = null;
};
// 上传文件
const handleUpload = async () => {
try {
// 表单验证
await formRef.value.validate();
if (!formState.file) {
message.error('请选择基准文件');
return;
}
uploading.value = true;
const response = await evaluationApi.uploadBenchmark(
props.databaseId,
formState.file,
{
name: formState.name,
description: formState.description
}
);
if (response.message === 'success') {
message.success('上传成功');
handleCancel();
emit('success');
} else {
message.error(response.message || '上传失败');
}
} catch (error) {
console.error('上传失败:', error);
message.error('上传失败');
} finally {
uploading.value = false;
}
};
// 取消操作
const handleCancel = () => {
visible.value = false;
resetForm();
};
// 重置表单
const resetForm = () => {
formRef.value?.resetFields();
fileList.value = [];
formState.file = null;
uploading.value = false;
};
// 监听visible变化
watch(visible, (val) => {
if (!val) {
resetForm();
}
});
</script>
<style lang="less" scoped>
:deep(.ant-upload-dragger) {
.ant-upload-text {
font-size: 16px;
color: var(--gray-700);
.anticon {
font-size: 48px;
color: var(--gray-400);
margin-bottom: 16px;
}
}
.ant-upload-hint {
color: var(--gray-500);
}
}
</style>