gongxiangp2/lib/work-review-client.ts

145 lines
4.7 KiB
TypeScript

'use client'
import { chunkUpload } from '@/lib/chunk-upload'
import { preparePerformanceSurveyImagesInBrowser } from '@/lib/performance-survey-image-browser'
import type {
WorkReviewBoardData,
WorkReviewProcessedImage,
WorkReviewProject,
WorkReviewReviewInput,
WorkReviewStatus,
WorkReviewSubmissionInput,
} from '@/types/work-review'
const API_BASE = '/api/work-review'
export class WorkReviewRequestError extends Error {
status: number
constructor(message: string, status: number) {
super(message)
this.status = status
}
}
async function workReviewRequest<T>(path: string, options?: RequestInit): Promise<T> {
let response: Response
try {
response = await fetch(`${API_BASE}${path}`, {
...options,
cache: options?.method && options.method !== 'GET' ? options.cache : 'no-store',
headers: {
...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),
...(options?.headers || {}),
},
})
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') throw error
throw new WorkReviewRequestError('网络连接失败,请检查网络后重试', 0)
}
const contentType = response.headers.get('content-type') || ''
const payload = contentType.includes('application/json')
? await response.json().catch(() => null)
: await response.text().catch(() => '')
if (!response.ok || (payload && typeof payload === 'object' && payload.success === false)) {
const message = payload && typeof payload === 'object'
? String(payload.error || payload.message || '请求失败')
: String(payload || '请求失败')
throw new WorkReviewRequestError(message, response.status)
}
return (payload && typeof payload === 'object' && 'data' in payload ? payload.data : payload) as T
}
export function getWorkReviewBoard(
input: {
query?: string
status?: WorkReviewStatus | ''
project?: string
page?: number
pageSize?: number
},
signal?: AbortSignal,
) {
const params = new URLSearchParams()
if (input.query?.trim()) params.set('query', input.query.trim())
if (input.status) params.set('status', input.status)
if (input.project?.trim()) params.set('project', input.project.trim())
params.set('page', String(input.page || 1))
params.set('pageSize', String(input.pageSize || 20))
return workReviewRequest<WorkReviewBoardData>(`?${params.toString()}`, { signal })
}
export function createWorkReviewProject(input: WorkReviewSubmissionInput) {
return workReviewRequest<WorkReviewProject>('/projects', {
method: 'POST',
body: JSON.stringify(input),
})
}
export function createWorkReviewVersion(projectId: string, input: WorkReviewSubmissionInput) {
return workReviewRequest<WorkReviewProject>(`/projects/${encodeURIComponent(projectId)}/versions`, {
method: 'POST',
body: JSON.stringify(input),
})
}
export function submitWorkReview(versionId: string, input: WorkReviewReviewInput) {
return workReviewRequest<WorkReviewProject>(`/versions/${encodeURIComponent(versionId)}/reviews`, {
method: 'POST',
body: JSON.stringify(input),
})
}
export async function uploadWorkReviewImages(
files: File[],
onProgress?: (message: string, percent?: number) => void,
): Promise<WorkReviewProcessedImage[]> {
if (files.length === 0) throw new Error('请至少选择 1 张作品图片')
const processed = await preparePerformanceSurveyImagesInBrowser(
files,
(message) => {
onProgress?.(message)
},
{ profile: 'work_review' },
)
const uploaded: WorkReviewProcessedImage[] = []
for (let index = 0; index < processed.length; index += 1) {
const image = processed[index]
onProgress?.(`正在上传图片 ${index + 1}/${processed.length}`, Math.round((index / processed.length) * 100))
const result = await chunkUpload(
image.file,
{
materialType: '作品过稿看板',
fileName: image.file.name,
},
{
skipComplete: true,
onProgress: (fileProgress) => {
const totalProgress = ((index + fileProgress / 100) / processed.length) * 100
onProgress?.(`正在上传图片 ${index + 1}/${processed.length}`, Math.round(totalProgress))
},
},
)
if (!result?.uploadId) throw new Error(`图片「${image.sourceName}」上传失败,请重试`)
uploaded.push({
uploadId: result.uploadId,
mimeType: image.mimeType,
width: image.width,
height: image.height,
kind: image.kind,
sourceName: image.sourceName,
sourceIndex: image.sourceIndex,
sliceIndex: image.sliceIndex,
sliceCount: image.sliceCount,
})
}
onProgress?.('图片上传完成,正在保存作品', 100)
return uploaded
}