upload 1
All checks were successful
Deploy to Server / deploy (push) Successful in 28s

This commit is contained in:
jeremygan2021
2026-03-22 22:43:59 +08:00
parent bb373f3c60
commit 77b8376878
2 changed files with 141 additions and 46 deletions

View File

@@ -343,6 +343,8 @@ class ReplyViewSet(viewsets.ModelViewSet):
return Response({'liked': liked, 'count': obj.likes.count()}) return Response({'liked': liked, 'count': obj.likes.count()})
import requests import requests
import warnings
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
class TopicMediaViewSet(viewsets.ViewSet): class TopicMediaViewSet(viewsets.ViewSet):
""" """
@@ -367,7 +369,8 @@ class TopicMediaViewSet(viewsets.ViewSet):
try: try:
# 这里的 headers 不需要 Content-Typerequests 会自动设置 multipart/form-data # 这里的 headers 不需要 Content-Typerequests 会自动设置 multipart/form-data
response = requests.post(upload_url, files=files, timeout=30) # 注意: verify=False 跳过SSL证书验证data.tangledup-ai.com 证书已过期)
response = requests.post(upload_url, files=files, timeout=30, verify=False)
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()

View File

@@ -31,6 +31,9 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
const [form] = Form.useForm(); const [form] = Form.useForm();
const [uploadedFiles, setUploadedFiles] = useState([]); const [uploadedFiles, setUploadedFiles] = useState([]);
const [uploadingFiles, setUploadingFiles] = useState({}); const [uploadingFiles, setUploadingFiles] = useState({});
const [pendingCoverImage, setPendingCoverImage] = useState(null);
const [pendingAttachments, setPendingAttachments] = useState([]);
const [isCreatingProject, setIsCreatingProject] = useState(false);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
useEffect(() => { useEffect(() => {
@@ -66,30 +69,6 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
} }
}, [initialValues, form]); }, [initialValues, form]);
const createMutation = useMutation({
mutationFn: createProject,
onSuccess: () => {
message.success('项目创建成功');
queryClient.invalidateQueries(['projects']);
onSuccess();
},
onError: (error) => {
message.error(`创建失败: ${error.response?.data?.detail || error.message}`);
}
});
const updateMutation = useMutation({
mutationFn: (data) => updateProject(initialValues.id, data),
onSuccess: () => {
message.success('项目更新成功');
queryClient.invalidateQueries(['projects']);
onSuccess();
},
onError: (error) => {
message.error(`更新失败: ${error.response?.data?.detail || error.message}`);
}
});
const handleUpload = ({ file, onSuccess, onError }) => { const handleUpload = ({ file, onSuccess, onError }) => {
console.log('handleUpload called', file.name); console.log('handleUpload called', file.name);
@@ -144,17 +123,80 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
}); });
}; };
const onFinish = (values) => { const uploadPendingFiles = async (projectId) => {
const uploadedFilesList = [];
if (pendingCoverImage) {
try {
const formData = new FormData();
formData.append('file', pendingCoverImage);
const res = await uploadProjectFile(formData);
const imageUrl = res.data.file_url_display || res.data.file_url;
await updateProject(projectId, { cover_image_url: imageUrl });
setPendingCoverImage(null);
} catch (err) {
console.error('封面上传失败:', err);
}
}
for (const file of pendingAttachments) {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('project', projectId);
const res = await uploadProjectFile(formData);
uploadedFilesList.push({
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'
});
} catch (err) {
console.error('文件上传失败:', err);
message.error(`文件 ${file.name} 上传失败`);
}
}
if (uploadedFilesList.length > 0) {
setUploadedFiles(prev => [...prev, ...uploadedFilesList]);
}
setPendingAttachments([]);
};
const onFinish = async (values) => {
setIsCreatingProject(true);
const data = { const data = {
...values, ...values,
competition: competitionId, competition: competitionId,
}; };
if (initialValues?.id) { if (initialValues?.id) {
updateMutation.mutate(data); await updateProject(initialValues.id, data);
if (pendingAttachments.length > 0) {
await uploadPendingFiles(initialValues.id);
}
message.success('项目更新成功');
queryClient.invalidateQueries(['projects']);
onSuccess();
} else { } else {
createMutation.mutate(data); try {
const res = await createProject(data);
const projectId = res.data.id;
if (pendingAttachments.length > 0 || pendingCoverImage) {
await uploadPendingFiles(projectId);
}
message.success('项目创建成功');
queryClient.invalidateQueries(['projects']);
onSuccess();
} catch (error) {
message.error(`创建失败: ${error.response?.data?.detail || error.message}`);
}
} }
setIsCreatingProject(false);
}; };
return ( return (
@@ -205,12 +247,13 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
label="封面图片" label="封面图片"
extra="支持上传本地图片自动转换为URL" extra="支持上传本地图片自动转换为URL"
> >
{initialValues ? ( <Upload
<Upload showUploadList={false}
showUploadList={false} accept="image/*"
accept="image/*" beforeUpload={(file) => {
beforeUpload={(file) => { console.log('Cover image selected:', file.name);
console.log('Cover image selected:', file.name);
if (initialValues?.id) {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
@@ -223,13 +266,21 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
.catch(err => { .catch(err => {
message.error(`上传失败: ${err.response?.data?.detail || err.message}`); message.error(`上传失败: ${err.response?.data?.detail || err.message}`);
}); });
} else {
return Upload.LIST_IGNORE; setPendingCoverImage(file);
}} const reader = new FileReader();
> reader.onload = () => {
<Button icon={<CloudUploadOutlined />}>选择图片</Button> form.setFieldsValue({ cover_image_url: reader.result });
</Upload> };
) : null} reader.readAsDataURL(file);
message.info('封面已选择,提交时将自动上传');
}
return Upload.LIST_IGNORE;
}}
>
<Button icon={<CloudUploadOutlined />}>选择图片</Button>
</Upload>
<Input <Input
placeholder="上传图片或输入URL" placeholder="上传图片或输入URL"
style={{ marginTop: 8 }} style={{ marginTop: 8 }}
@@ -237,10 +288,41 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
/> />
</Form.Item> </Form.Item>
{initialValues?.id && ( {(
<Form.Item label="项目附件 (PPT/PDF/视频/图片)"> <Form.Item label="项目附件 (PPT/PDF/视频/图片)">
{!initialValues?.id && pendingAttachments.length > 0 && (
<div style={{ marginBottom: 16 }}>
<div style={{ color: '#999', fontStyle: 'italic', marginBottom: 8 }}>待上传文件</div>
<Space orientation="vertical" style={{ width: '100%' }} size="middle">
{pendingAttachments.map((file, index) => (
<div key={index} style={{
display: 'flex',
alignItems: 'center',
padding: '12px 16px',
background: '#fff7e6',
borderRadius: 8,
border: '1px solid #ffd591'
}}>
<span style={{ fontSize: 20, marginRight: 12 }}>
{getFileIcon(file.name.split('.').pop()?.toLowerCase())}
</span>
<span style={{ flex: 1, fontWeight: 500 }}>{file.name}</span>
<Button
type="link"
danger
onClick={() => {
setPendingAttachments(prev => prev.filter((_, i) => i !== index));
}}
>
删除
</Button>
</div>
))}
</Space>
</div>
)}
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
{uploadedFiles.length === 0 ? ( {uploadedFiles.length === 0 && initialValues?.id ? (
<div style={{ color: '#999', fontStyle: 'italic' }}>暂无上传文件</div> <div style={{ color: '#999', fontStyle: 'italic' }}>暂无上传文件</div>
) : ( ) : (
<Space orientation="vertical" style={{ width: '100%' }} size="middle"> <Space orientation="vertical" style={{ width: '100%' }} size="middle">
@@ -307,8 +389,18 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
message.error('文件大小不能超过 50MB'); message.error('文件大小不能超过 50MB');
return Upload.LIST_IGNORE; return Upload.LIST_IGNORE;
} }
console.log('beforeUpload passed, manually calling handleUpload');
handleUpload({ file, onSuccess: () => {}, onError: () => {} }); if (initialValues?.id) {
console.log('beforeUpload passed, manually calling handleUpload');
handleUpload({ file, onSuccess: () => {}, onError: () => {} });
} else {
if (pendingAttachments.length >= 5) {
message.warning('最多只能上传5个文件');
return Upload.LIST_IGNORE;
}
setPendingAttachments(prev => [...prev, file]);
message.info('文件已添加到待上传列表,提交时将自动上传');
}
return Upload.LIST_IGNORE; return Upload.LIST_IGNORE;
}} }}
> >
@@ -320,7 +412,7 @@ const ProjectSubmission = ({ competitionId, initialValues, onCancel, onSuccess }
<Form.Item> <Form.Item>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}> <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
<Button onClick={onCancel}>取消</Button> <Button onClick={onCancel}>取消</Button>
<Button type="primary" htmlType="submit" loading={createMutation.isLoading || updateMutation.isLoading}> <Button type="primary" htmlType="submit" loading={isCreatingProject}>
{initialValues?.id ? '保存修改' : '保存草稿'} {initialValues?.id ? '保存修改' : '保存草稿'}
</Button> </Button>
{initialValues?.id && ( {initialValues?.id && (