Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions backend/app/routers/modeling_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,15 @@ async def modeling(
logger.warning("跳过空文件名")
continue

content = await file.read()
if not content:
logger.warning(f"文件 {file.filename} 内容为空")
continue

# 流式写入,避免一次性读取大文件
with open(data_file_path, "wb") as f:
f.write(content)
chunk_size = 1024 * 1024
while True:
chunk = await file.read(chunk_size)
if not chunk:
break
f.write(chunk)
await file.close()
logger.info(f"成功保存文件: {data_file_path}")

except Exception as e:
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/apis/submitModelingApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import request from "@/utils/request";
import type { AxiosProgressEvent } from "axios";

/**
* 提交数学建模任务
Expand All @@ -12,6 +13,7 @@ export function submitModelingTask(
format_output?: string;
},
files?: File[],
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void,
) {
const formData = new FormData();
// 添加问题数据
Expand All @@ -36,7 +38,11 @@ export function submitModelingTask(
headers: {
"Content-Type": "multipart/form-data",
},
timeout: 30000, // 添加超时设置
onUploadProgress: (event) => {
if (!onUploadProgress) return;
onUploadProgress(event);
},
timeout: 300000,
});
}
}
73 changes: 55 additions & 18 deletions frontend/src/components/UserStepper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { saveApiConfig } from "@/apis/apiKeyApi";
import { submitModelingTask } from "@/apis/submitModelingApi";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import type { AxiosProgressEvent } from "axios";
import {
Select,
SelectContent,
Expand Down Expand Up @@ -77,6 +78,13 @@ const taskId = ref<string | null>(null);
/** 文件输入元素引用 */
const fileInput = ref<HTMLInputElement | null>(null);

/** 是否正在提交 */
const isSubmitting = ref(false);

/** 上传进度百分比 */
const uploadProgress = ref(0);


// ---- Methods ----

const nextStep = () => {
Expand All @@ -102,6 +110,16 @@ const handleFileUpload = (event: Event) => {

const router = useRouter();

const handleUploadProgress = (progressEvent: AxiosProgressEvent) => {
const loaded = progressEvent.loaded ?? 0;
const total = progressEvent.total ?? 0;

if (total > 0) {
const nextProgress = Math.min(100, Math.round((loaded / total) * 100));
uploadProgress.value = Math.max(uploadProgress.value, nextProgress);
}
};

/** 提交建模任务 */
const handleSubmit = async () => {
try {
Expand Down Expand Up @@ -140,14 +158,18 @@ const handleSubmit = async () => {
console.log(selectedOptions.value);
console.log(question.value);
console.log(uploadedFiles.value);
isSubmitting.value = true;
uploadProgress.value = 0;
const response = await submitModelingTask(
{
ques_all: question.value,
comp_template: selectedOptions.value.template,
format_output: selectedOptions.value.format,
},
uploadedFiles.value,
handleUploadProgress,
);
uploadProgress.value = 100;

taskId.value = response?.data?.task_id ?? null;
taskStore.addUserMessage(question.value);
Expand All @@ -168,6 +190,8 @@ const handleSubmit = async () => {
description: "请检查 API Key 是否正确",
variant: "destructive",
});
} finally {
isSubmitting.value = false;
}
};
</script>
Expand Down Expand Up @@ -199,7 +223,7 @@ const handleSubmit = async () => {
</div>
</Transition>

<div class="border rounded-lg shadow-sm">
<div class="border rounded-lg shadow-sm">
<!-- Step 1: File Upload -->
<div v-if="currentStep === 1" class="p-6">
<div
Expand All @@ -210,21 +234,21 @@ const handleSubmit = async () => {
<div class="mx-auto w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center">
<FileUp class="w-6 h-6 text-primary" />
</div>
<div>
<p class="text-lg font-medium">拖拽数据集到此处或点击上传</p>
<p class="text-sm text-muted-foreground mt-1">
支持 .txt, .csv, .xlsx 等格式文件(可多选)
</p>
<div v-if="uploadedFiles.length > 0" class="text-sm text-green-600 mt-1">
已上传文件:
<ul>
<li v-for="(file, index) in uploadedFiles" :key="index">
{{ file.name }}
</li>
</ul>
</div>
</div>
</div>
<div>
<p class="text-lg font-medium">拖拽数据集到此处或点击上传</p>
<p class="text-sm text-muted-foreground mt-1">
支持 .txt, .csv, .xlsx 等格式文件(可多选)
</p>
<div v-if="uploadedFiles.length > 0" class="text-sm text-green-600 mt-1">
已上传文件:
<ul>
<li v-for="file in uploadedFiles" :key="file.name">
{{ file.name }}
</li>
</ul>
</div>
</div>
</div>
<div class="mt-4 flex justify-end">
<Button :disabled="!fileUploaded" @click="nextStep" size="sm">
下一步
Expand All @@ -239,7 +263,20 @@ const handleSubmit = async () => {
<h4 class="text-sm font-medium mb-2">粘贴完整题目</h4>
<Textarea v-model="question" placeholder="PDF 中完整题目背景和多个小问" class="min-h-[120px]" />
</div>


<div v-if="isSubmitting" class="space-y-2 rounded-lg border p-3">
<div class="flex items-center justify-between text-sm">
<span class="font-medium">上传进度</span>
<span>{{ uploadProgress }}%</span>
</div>
<div class="h-2 overflow-hidden rounded-full bg-muted">
<div
class="h-full bg-primary transition-all duration-300"
:style="{ width: `${uploadProgress}%` }"
/>
</div>
<p class="text-xs text-muted-foreground">正在上传,请稍候...</p>
</div>
<div class="grid grid-cols-3 gap-3">
<div v-for="item in selectConfig" :key="item.key">
<Select v-model="selectedOptions[item.key.toLowerCase() as keyof typeof selectedOptions]"
Expand All @@ -263,7 +300,7 @@ const handleSubmit = async () => {
<Button variant="outline" @click="prevStep" size="sm">
上一步
</Button>
<Button @click="handleSubmit" size="sm">
<Button :disabled="isSubmitting" @click="handleSubmit" size="sm">
开始分析
</Button>
</div>
Expand Down