diff --git a/backend/app/api/tasks.py b/backend/app/api/tasks.py index 904e1c3..98565d8 100644 --- a/backend/app/api/tasks.py +++ b/backend/app/api/tasks.py @@ -68,6 +68,7 @@ def _task_to_response(task: Task) -> TaskResponse: id=task.project.id, name=task.project.name, brand_name=task.project.brand.name if task.project.brand else None, + platform=task.project.platform, ), agency=AgencyInfo( id=task.agency.id, diff --git a/backend/app/schemas/task.py b/backend/app/schemas/task.py index 797e39e..95e20ea 100644 --- a/backend/app/schemas/task.py +++ b/backend/app/schemas/task.py @@ -87,6 +87,7 @@ class ProjectInfo(BaseModel): id: str name: str brand_name: Optional[str] = None + platform: Optional[str] = None class TaskResponse(BaseModel): diff --git a/frontend/app/agency/appeals/page.tsx b/frontend/app/agency/appeals/page.tsx index 02edb2e..c22af79 100644 --- a/frontend/app/agency/appeals/page.tsx +++ b/frontend/app/agency/appeals/page.tsx @@ -149,7 +149,7 @@ function mapTaskToAppeal(task: TaskResponse): Appeal { taskTitle: task.name, creatorId: task.creator.id, creatorName: task.creator.name, - platform: 'douyin', // Backend does not expose platform on task; default for now + platform: task.project?.platform || 'douyin', type, contentType, reason: task.appeal_reason || '申诉', diff --git a/frontend/app/agency/briefs/[id]/page.tsx b/frontend/app/agency/briefs/[id]/page.tsx index e0383db..e25659c 100644 --- a/frontend/app/agency/briefs/[id]/page.tsx +++ b/frontend/app/agency/briefs/[id]/page.tsx @@ -356,7 +356,7 @@ export default function BriefConfigPage() { id: brief?.id || `no-brief-${projectId}`, projectName: project.name, brandName: project.brand_name || '未知品牌', - platform: 'douyin', // 后端暂无 platform 字段 + platform: project.platform || 'douyin', files: briefFiles, brandRules: { restrictions: brief?.other_requirements || '暂无限制条件', @@ -418,15 +418,37 @@ export default function BriefConfigPage() { } try { const signedUrl = await api.getSignedUrl(file.url) - window.open(signedUrl, '_blank') + // 使用 a 标签触发下载 + const a = document.createElement('a') + a.href = signedUrl + a.target = '_blank' + a.rel = 'noopener noreferrer' + a.download = file.name + document.body.appendChild(a) + a.click() + document.body.removeChild(a) } catch { toast.error('获取下载链接失败') } } // 预览文件 - const handlePreview = (file: BriefFile) => { + const [previewUrl, setPreviewUrl] = useState(null) + const [previewLoading, setPreviewLoading] = useState(false) + const handlePreview = async (file: BriefFile) => { setPreviewFile(file) + setPreviewUrl(null) + if (!USE_MOCK && file.url) { + setPreviewLoading(true) + try { + const signedUrl = await api.getSignedUrl(file.url) + setPreviewUrl(signedUrl) + } catch { + toast.error('获取预览链接失败') + } finally { + setPreviewLoading(false) + } + } } // 导出平台规则文档 @@ -622,8 +644,22 @@ export default function BriefConfigPage() { })) } - const handlePreviewAgencyFile = (file: AgencyFile) => { + const [previewAgencyUrl, setPreviewAgencyUrl] = useState(null) + const [previewAgencyLoading, setPreviewAgencyLoading] = useState(false) + const handlePreviewAgencyFile = async (file: AgencyFile) => { setPreviewAgencyFile(file) + setPreviewAgencyUrl(null) + if (!USE_MOCK && file.url) { + setPreviewAgencyLoading(true) + try { + const signedUrl = await api.getSignedUrl(file.url) + setPreviewAgencyUrl(signedUrl) + } catch { + toast.error('获取预览链接失败') + } finally { + setPreviewAgencyLoading(false) + } + } } const handleDownloadAgencyFile = async (file: AgencyFile) => { @@ -633,7 +669,14 @@ export default function BriefConfigPage() { } try { const signedUrl = await api.getSignedUrl(file.url) - window.open(signedUrl, '_blank') + const a = document.createElement('a') + a.href = signedUrl + a.target = '_blank' + a.rel = 'noopener noreferrer' + a.download = file.name + document.body.appendChild(a) + a.click() + document.body.removeChild(a) } catch { toast.error('获取下载链接失败') } @@ -1179,20 +1222,44 @@ export default function BriefConfigPage() { {/* 文件预览弹窗(品牌方) */} setPreviewFile(null)} + onClose={() => { setPreviewFile(null); setPreviewUrl(null) }} title={previewFile?.name || '文件预览'} size="lg" >
-
-
- -

文件预览区域

-

实际开发中将嵌入文件预览组件

-
+
+ {previewLoading ? ( +
+ + 加载预览中... +
+ ) : previewUrl && previewFile?.name.toLowerCase().endsWith('.pdf') ? ( +