diff --git a/frontend/src/components/competition/ProjectSubmission.jsx b/frontend/src/components/competition/ProjectSubmission.jsx
index 19e5a0d..8ac9376 100644
--- a/frontend/src/components/competition/ProjectSubmission.jsx
+++ b/frontend/src/components/competition/ProjectSubmission.jsx
@@ -30,8 +30,11 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
const [pendingCoverImage, setPendingCoverImage] = useState(null);
const [pendingAttachments, setPendingAttachments] = useState([]);
const [isCreatingProject, setIsCreatingProject] = useState(false);
+ const [currentProjectId, setCurrentProjectId] = useState(null);
const queryClient = useQueryClient();
+ const activeProjectId = currentProjectId || initialValues?.id;
+
useEffect(() => {
if (initialValues?.id) {
getProjects({ competition: competitionId, contestant: initialValues.id })
@@ -65,60 +68,6 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
}
}, [initialValues, form]);
- const handleUpload = ({ file, onSuccess, onError }) => {
- console.log('handleUpload called', file.name);
-
- if (!initialValues?.id) {
- message.warning('请先保存项目基本信息再上传文件');
- onError(new Error('请先保存项目'));
- return;
- }
-
- const fileUid = file.uid || Date.now().toString();
- console.log('fileUid:', fileUid);
-
- setUploadingFiles(prev => ({
- ...prev,
- [fileUid]: { percent: 0, status: 'uploading' }
- }));
-
- const formData = new FormData();
- formData.append('file', file);
- formData.append('project', initialValues.id);
- console.log('Sending upload request for project:', initialValues.id);
-
- uploadProjectFile(formData)
- .then(res => {
- console.log('Upload success:', res);
- setUploadingFiles(prev => ({
- ...prev,
- [fileUid]: { percent: 100, status: 'done' }
- }));
-
- const newFile = {
- uid: res.data.id,
- id: res.data.id,
- name: res.data.name || file.name,
- url: res.data.file_url_display || res.data.file_url,
- fileType: res.data.file_type,
- status: 'done'
- };
-
- setUploadedFiles(prev => [...prev, newFile]);
- message.success('文件上传成功');
- onSuccess(res.data);
- })
- .catch(err => {
- console.error('Upload error:', err);
- setUploadingFiles(prev => ({
- ...prev,
- [fileUid]: { percent: 0, status: 'error' }
- }));
- message.error(`上传失败: ${err.response?.data?.detail || err.message}`);
- onError(err);
- });
- };
-
const uploadPendingFiles = async (projectId) => {
const uploadedFilesList = [];
@@ -288,29 +237,61 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
showUploadList={false}
accept="image/*"
beforeUpload={(file) => {
- console.log('Cover image selected:', file.name);
+ console.log('Cover image selected:', file.name, 'activeProjectId:', activeProjectId);
- if (initialValues?.id) {
+ const doUpload = (projectId) => {
const formData = new FormData();
formData.append('file', file);
uploadProjectFile(formData)
.then(res => {
const imageUrl = res.data.file_url_display || res.data.file_url;
- form.setFieldsValue({ cover_image_url: imageUrl });
- message.success('封面上传成功');
+ updateProject(projectId, { cover_image_url: imageUrl })
+ .then(() => {
+ form.setFieldsValue({ cover_image_url: imageUrl });
+ message.success('封面上传成功');
+ })
+ .catch(err => {
+ console.error('更新封面失败:', err);
+ message.error(`更新封面失败: ${err.response?.data?.detail || err.message}`);
+ });
})
.catch(err => {
- message.error(`上传失败: ${err.response?.data?.detail || err.message}`);
+ console.error('封面上传失败:', err);
+ const errorData = err.response?.data;
+ let errorMsg = '上传失败';
+ if (errorData) {
+ if (typeof errorData === 'object') {
+ errorMsg = Object.entries(errorData).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : v}`).join('; ');
+ } else {
+ errorMsg = String(errorData);
+ }
+ }
+ message.error(`上传失败: ${errorMsg}`);
});
+ };
+
+ if (activeProjectId) {
+ doUpload(activeProjectId);
} else {
- setPendingCoverImage(file);
- const reader = new FileReader();
- reader.onload = () => {
- form.setFieldsValue({ cover_image_url: reader.result });
+ message.loading('正在创建项目...', 0);
+ const tempData = {
+ title: '临时草稿',
+ description: '临时草稿',
+ competition: competitionId,
};
- reader.readAsDataURL(file);
- message.info('封面已选择,提交时将自动上传');
+ createProject(tempData)
+ .then(res => {
+ message.destroy();
+ const newProjectId = res.data.id;
+ setCurrentProjectId(newProjectId);
+ doUpload(newProjectId);
+ })
+ .catch(err => {
+ message.destroy();
+ console.error('创建临时项目失败:', err);
+ message.error(`创建项目失败: ${err.response?.data?.detail || err.message}`);
+ });
}
return Upload.LIST_IGNORE;
@@ -412,7 +393,7 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
maxCount={5}
accept=".ppt,.pptx,.pdf,.mp4,.mov,.avi,.webm,.jpg,.jpeg,.png,.gif,.webp,.doc,.docx"
beforeUpload={(file) => {
- console.log('beforeUpload triggered for:', file.name, 'initialValues:', initialValues);
+ console.log('beforeUpload triggered for:', file.name, 'activeProjectId:', activeProjectId);
const allowedExtensions = ['ppt', 'pptx', 'pdf', 'mp4', 'mov', 'avi', 'webm', 'jpg', 'jpeg', 'png', 'gif', 'webp', 'doc', 'docx'];
const fileExt = file.name.split('.').pop()?.toLowerCase();
@@ -427,19 +408,79 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
return Upload.LIST_IGNORE;
}
- const hasProjectId = initialValues?.id;
- console.log('hasProjectId:', hasProjectId);
+ const doUpload = (projectId) => {
+ const fileUid = file.uid || Date.now().toString();
+ setUploadingFiles(prev => ({
+ ...prev,
+ [fileUid]: { percent: 0, status: 'uploading' }
+ }));
+
+ const formData = new FormData();
+ formData.append('file', file);
+ formData.append('project', projectId);
+
+ uploadProjectFile(formData)
+ .then(res => {
+ console.log('Upload success:', res);
+ setUploadingFiles(prev => ({
+ ...prev,
+ [fileUid]: { percent: 100, status: 'done' }
+ }));
+
+ const newFile = {
+ uid: res.data.id,
+ id: res.data.id,
+ name: res.data.name || file.name,
+ url: res.data.file_url_display || res.data.file_url,
+ fileType: res.data.file_type,
+ status: 'done'
+ };
+
+ setUploadedFiles(prev => [...prev, newFile]);
+ message.success('文件上传成功');
+ })
+ .catch(err => {
+ console.error('Upload error:', err);
+ setUploadingFiles(prev => ({
+ ...prev,
+ [fileUid]: { percent: 0, status: 'error' }
+ }));
+ const errorData = err.response?.data;
+ let errorMsg = '上传失败';
+ if (errorData) {
+ if (typeof errorData === 'object') {
+ errorMsg = Object.entries(errorData).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : v}`).join('; ');
+ } else {
+ errorMsg = String(errorData);
+ }
+ }
+ message.error(`上传失败: ${errorMsg}`);
+ });
+ };
- if (hasProjectId) {
- console.log('beforeUpload passed, manually calling handleUpload');
- handleUpload({ file, onSuccess: () => {}, onError: () => {} });
+ if (activeProjectId) {
+ console.log('Uploading to existing project');
+ doUpload(activeProjectId);
} else {
- if (pendingAttachments.length >= 5) {
- message.warning('最多只能上传5个文件');
- return Upload.LIST_IGNORE;
- }
- setPendingAttachments(prev => [...prev, file]);
- message.info('文件已添加到待上传列表,提交时将自动上传');
+ console.log('Creating temp project first');
+ message.loading('正在创建项目...', 0);
+ const tempData = {
+ title: '临时草稿',
+ description: '临时草稿',
+ competition: competitionId,
+ };
+ createProject(tempData)
+ .then(res => {
+ message.destroy();
+ const newProjectId = res.data.id;
+ setCurrentProjectId(newProjectId);
+ doUpload(newProjectId);
+ })
+ .catch(err => {
+ message.destroy();
+ console.error('创建临时项目失败:', err);
+ message.error(`创建项目失败: ${err.response?.data?.detail || err.message}`);
+ });
}
return Upload.LIST_IGNORE;
}}
@@ -455,7 +496,7 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
- {initialValues?.id && (
+ {(initialValues?.id || currentProjectId) && (