"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}
) }