Your Name 54eaa54966 feat: 前端全面对接后端 API(Phase 1 完成)
- 新增基础设施:useOSSUpload Hook、SSEContext Provider、taskStageMapper 工具
- 达人端4页面:任务列表/详情/脚本上传/视频上传对接真实 API
- 代理商端3页面:工作台/审核队列/审核详情对接真实 API
- 品牌方端4页面:项目列表/创建项目/项目详情/Brief配置对接真实 API
- 保留 USE_MOCK 开关,mock 模式下使用类型安全的 mock 数据
- 所有页面添加 loading 骨架屏、SSE 实时更新、错误处理

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 15:58:47 +08:00

113 lines
3.2 KiB
TypeScript

'use client'
import { useState, useCallback } from 'react'
import { api } from '@/lib/api'
import { USE_MOCK } from '@/contexts/AuthContext'
interface UploadResult {
url: string
file_key: string
file_name: string
file_size: number
}
interface UseOSSUploadReturn {
upload: (file: File) => Promise<UploadResult>
isUploading: boolean
progress: number
error: string | null
reset: () => void
}
export function useOSSUpload(fileType: string = 'general'): UseOSSUploadReturn {
const [isUploading, setIsUploading] = useState(false)
const [progress, setProgress] = useState(0)
const [error, setError] = useState<string | null>(null)
const reset = useCallback(() => {
setIsUploading(false)
setProgress(0)
setError(null)
}, [])
const upload = useCallback(async (file: File): Promise<UploadResult> => {
setIsUploading(true)
setProgress(0)
setError(null)
try {
if (USE_MOCK) {
// Mock 模式:模拟 2 秒上传
for (let i = 0; i <= 100; i += 20) {
await new Promise(r => setTimeout(r, 400))
setProgress(i)
}
const result: UploadResult = {
url: `https://mock-oss.example.com/${fileType}/${Date.now()}_${file.name}`,
file_key: `${fileType}/${Date.now()}_${file.name}`,
file_name: file.name,
file_size: file.size,
}
setProgress(100)
setIsUploading(false)
return result
}
// 1. 获取上传凭证
setProgress(10)
const policy = await api.getUploadPolicy(fileType)
// 2. 构建 OSS 直传 FormData
const fileKey = `${policy.dir}${Date.now()}_${file.name}`
const formData = new FormData()
formData.append('key', fileKey)
formData.append('policy', policy.policy)
formData.append('OSSAccessKeyId', policy.access_key_id)
formData.append('signature', policy.signature)
formData.append('success_action_status', '200')
formData.append('file', file)
// 3. 上传到 OSS
setProgress(30)
const xhr = new XMLHttpRequest()
await new Promise<void>((resolve, reject) => {
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
setProgress(30 + Math.round((e.loaded / e.total) * 50))
}
}
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve()
} else {
reject(new Error(`上传失败: ${xhr.status}`))
}
}
xhr.onerror = () => reject(new Error('网络错误'))
xhr.open('POST', policy.host)
xhr.send(formData)
})
// 4. 回调通知后端
setProgress(90)
const result = await api.fileUploaded(fileKey, file.name, file.size, fileType)
setProgress(100)
setIsUploading(false)
return {
url: result.url,
file_key: result.file_key,
file_name: result.file_name,
file_size: result.file_size,
}
} catch (err) {
const message = err instanceof Error ? err.message : '上传失败'
setError(message)
setIsUploading(false)
throw err
}
}, [fileType])
return { upload, isUploading, progress, error, reset }
}