'use client'; import { useRef, useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Loader2, UploadCloud } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Progress } from '@/components/ui/progress'; import { PagesService } from '@/lib/services/openflare'; import { cn } from '@/lib/utils'; import { deploymentsQueryKey, formatBytes, projectQueryKey, projectsQueryKey, } from './pages-utils'; interface DeploymentUploadDialogProps { open: boolean; onOpenChange: (open: boolean) => void; projectId: number; } export function DeploymentUploadDialog({ open, onOpenChange, projectId, }: DeploymentUploadDialogProps) { const queryClient = useQueryClient(); const fileInputRef = useRef(null); const [file, setFile] = useState(null); const [isDragActive, setIsDragActive] = useState(false); const [uploadProgress, setUploadProgress] = useState(null); const resetForm = () => { setFile(null); setIsDragActive(false); setUploadProgress(null); if (fileInputRef.current) fileInputRef.current.value = ''; }; const handleClose = (nextOpen: boolean) => { if (!nextOpen) resetForm(); onOpenChange(nextOpen); }; const uploadMutation = useMutation({ mutationFn: () => { if (!file) throw new Error('请选择 zip 部署包'); return PagesService.uploadDeployment(projectId, { file, onProgress: setUploadProgress, }); }, onSuccess: async () => { toast.success('部署包上传成功'); await Promise.all([ queryClient.invalidateQueries({ queryKey: deploymentsQueryKey(projectId), }), queryClient.invalidateQueries({ queryKey: projectQueryKey(projectId) }), queryClient.invalidateQueries({ queryKey: projectsQueryKey }), ]); handleClose(false); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '上传失败'); setUploadProgress(null); }, }); const handleFileSelect = (selected: File | null) => { if (!selected) return; if (!selected.name.toLowerCase().endsWith('.zip')) { toast.error('仅支持 zip 格式的文件'); return; } setFile(selected); }; return ( 上传部署包 上传已构建的 zip 静态资源包,部署后可在列表中激活。
{ e.preventDefault(); setIsDragActive(true); }} onDragOver={(e) => e.preventDefault()} onDragLeave={(e) => { e.preventDefault(); setIsDragActive(false); }} onDrop={(e) => { e.preventDefault(); setIsDragActive(false); handleFileSelect(e.dataTransfer.files[0] ?? null); }} >

拖拽 zip 文件到此处,或点击选择文件

handleFileSelect(e.target.files?.[0] ?? null)} />
{file ? (

{file.name}

{formatBytes(file.size)}

) : null} {uploadProgress !== null ? (
{uploadProgress >= 100 ? '服务端处理中' : '上传进度'} {uploadProgress >= 100 ? '请稍候' : `${uploadProgress}%`}
= 100 ? 100 : uploadProgress} />
) : null}
); }