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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4a3c7e7923
commit
54eaa54966
@@ -0,0 +1,112 @@
|
||||
'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 }
|
||||
}
|
||||
Reference in New Issue
Block a user