"use client"; /** * ImageUpload — 공통 이미지 업로드 컴포넌트 * * 기능: * - 이미지 파일 선택 (클릭 or 드래그) * - 미리보기 표시 * - /api/files/upload API로 업로드 * - 업로드 후 파일 objid 반환 * - 기존 이미지 표시 (objid 또는 URL) * * 사용법: * setForm(...)} // 업로드 완료 시 objid 반환 * tableName="equipment_mng" // 연결 테이블 * recordId="xxx" // 연결 레코드 ID * columnName="image_path" // 연결 컬럼명 * /> */ import React, { useState, useRef, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Upload, X, Loader2, ImageIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { toast } from "sonner"; interface ImageUploadProps { /** 현재 이미지 값 (파일 objid, URL, 또는 빈 문자열) */ value?: string; /** 업로드 완료 시 콜백 (파일 objid) */ onChange?: (value: string) => void; /** 연결할 테이블명 */ tableName?: string; /** 연결할 레코드 ID */ recordId?: string; /** 연결할 컬럼명 */ columnName?: string; /** 높이 (기본 160px) */ height?: string; /** 비활성화 */ disabled?: boolean; className?: string; } export function ImageUpload({ value, onChange, tableName, recordId, columnName, height = "h-40", disabled = false, className, }: ImageUploadProps) { const [uploading, setUploading] = useState(false); const [dragOver, setDragOver] = useState(false); const fileRef = useRef(null); // 이미지 URL 결정 const imageUrl = value ? (value.startsWith("http") || value.startsWith("/")) ? value : `/api/files/preview/${value}` : null; const handleUpload = useCallback(async (file: File) => { if (!file.type.startsWith("image/")) { toast.error("이미지 파일만 업로드 가능합니다."); return; } if (file.size > 10 * 1024 * 1024) { toast.error("파일 크기는 10MB 이하만 가능합니다."); return; } setUploading(true); try { const formData = new FormData(); formData.append("files", file); formData.append("docType", "IMAGE"); formData.append("docTypeName", "이미지"); if (tableName) formData.append("linkedTable", tableName); if (recordId) formData.append("recordId", recordId); if (columnName) formData.append("columnName", columnName); if (tableName && recordId) { formData.append("autoLink", "true"); if (columnName) formData.append("isVirtualFileColumn", "true"); } const res = await apiClient.post("/files/upload", formData, { headers: { "Content-Type": "multipart/form-data" }, }); if (res.data?.success && (res.data.files?.length > 0 || res.data.data?.length > 0)) { const file = res.data.files?.[0] || res.data.data?.[0]; const objid = file.objid; onChange?.(objid); toast.success("이미지가 업로드되었습니다."); } else { toast.error(res.data?.message || "업로드에 실패했습니다."); } } catch (err: any) { console.error("이미지 업로드 실패:", err); toast.error(err.response?.data?.message || "업로드에 실패했습니다."); } finally { setUploading(false); } }, [tableName, recordId, columnName, onChange]); const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) handleUpload(file); e.target.value = ""; }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); const file = e.dataTransfer.files?.[0]; if (file) handleUpload(file); }; const handleRemove = () => { onChange?.(""); }; return (
!disabled && !uploading && fileRef.current?.click()} onDragOver={(e) => { e.preventDefault(); if (!disabled) setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={!disabled ? handleDrop : undefined} > {uploading ? (
업로드 중...
) : imageUrl ? ( 이미지 ) : (
클릭 또는 드래그하여 업로드
)}
{/* 삭제 버튼 */} {imageUrl && !disabled && ( )}
); }