diff --git a/offer_backend/apps/applications/views.py b/offer_backend/apps/applications/views.py index ae1a98f..b878f41 100644 --- a/offer_backend/apps/applications/views.py +++ b/offer_backend/apps/applications/views.py @@ -1,4 +1,7 @@ from rest_framework import generics, viewsets +from rest_framework.response import Response +from rest_framework.decorators import action +from django.http import FileResponse from .models import Application from .serializers import ApplicationCreateSerializer, ApplicationSerializer, ApplicationStatusSerializer from .emails import notify_status_change @@ -28,6 +31,26 @@ class ApplicationManageViewSet(viewsets.ReadOnlyModelViewSet): job__organization=user.organization ).select_related('job__organization', 'applicant') + @action(detail=True, methods=['get']) + def download_resume(self, request, pk=None): + """下载求职者的简历附件""" + application = self.get_object() + attachment_url = application.resume_snapshot.get('attachment_url') + + if not attachment_url: + return Response({'detail': '附件不可用'}, status=404) + + try: + from django.core.files.storage import default_storage + # 获取文件 + file = default_storage.open(attachment_url.lstrip('/media/'), 'rb') + response = FileResponse(file) + response['Content-Type'] = 'application/octet-stream' + response['Content-Disposition'] = f'attachment; filename="{application.resume_snapshot.get("name", "resume")}.pdf"' + return response + except Exception as e: + return Response({'detail': f'下载失败: {str(e)}'}, status=400) + class ApplicationStatusUpdateView(generics.UpdateAPIView): serializer_class = ApplicationStatusSerializer permission_classes = [IsAdminOrSuperAdmin] diff --git a/offer_frontend/src/views/admin/ApplicationManageView.vue b/offer_frontend/src/views/admin/ApplicationManageView.vue index 3f9b9b3..4fc373f 100644 --- a/offer_frontend/src/views/admin/ApplicationManageView.vue +++ b/offer_frontend/src/views/admin/ApplicationManageView.vue @@ -104,6 +104,7 @@ const applications = ref([]) const loading = ref(false) const resumeVisible = ref(false) const currentResume = ref(null) +const currentApplicationId = ref(null) const genderMap = { 'male': '男', @@ -128,6 +129,7 @@ async function updateStatus(row) { function viewResume(row) { currentResume.value = row.resume_snapshot + currentApplicationId.value = row.id resumeVisible.value = true } @@ -135,19 +137,29 @@ function getGenderLabel(gender) { return genderMap[gender] || '-' } -function downloadAttachment() { - if (!currentResume.value?.attachment_url) { - ElMessage.warning('附件不可用') +async function downloadAttachment() { + if (!currentApplicationId.value) { + ElMessage.warning('无法获取投递记录') return } - const link = document.createElement('a') - link.href = currentResume.value.attachment_url - link.download = `${currentResume.value.name}_resume` - link.style.display = 'none' - document.body.appendChild(link) - link.click() - document.body.removeChild(link) - ElMessage.success('下载已启动') + try { + const response = await fetch(`/api/applications/manage/${currentApplicationId.value}/download_resume/`) + if (!response.ok) throw new Error('下载失败') + + const blob = await response.blob() + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = `${currentResume.value.name}_resume.pdf` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + window.URL.revokeObjectURL(url) + ElMessage.success('下载已启动') + } catch (e) { + ElMessage.error('下载失败,请重试') + console.error(e) + } }