You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在next-cloudinary与react-hook-form中实现多图上传存储

多图上传解决方案(基于shadcn/ui、react-hook-form、Next-Cloudinary)

一、修改ImageUpload组件的Props和上传逻辑

调整组件支持数组形式的URL存储,同时增加多图展示、移除功能:

"use client";
import React, { useCallback } from "react";
import Image from "next/image";
import { CldUploadWidget } from "next-cloudinary";
import { ImagePlus, X } from "lucide-react";

declare global {
    var cloudinary: any;
}

// 调整Props:onChange接受字符串数组
interface ImageUploadProps {
    value: string[];
    onChange: (value: string[]) => void;
    disabled?: boolean;
}

const ImageUpload: React.FC<ImageUploadProps> = ({ value, onChange, disabled }) => {
    const handleUpload = useCallback(
        (result: any) => {
            // 修复Cloudinary字段拼写错误:scure_url -> secure_url
            const newUrl = result.info.secure_url;
            // 将新URL追加到现有数组,避免覆盖原有数据
            onChange([...value, newUrl]);
        },
        [onChange, value]
    );

    // 移除单张图片的逻辑
    const handleRemoveImage = (index: number) => {
        onChange(value.filter((_, i) => i !== index));
    };

    return (
        <div className="space-y-4">
            <CldUploadWidget
                onUpload={handleUpload}
                options={{ maxFiles: 5 }}
                uploadPreset="uxvsxycu"
                disabled={disabled}
            >
                {({ open }) => (
                    <div
                        onClick={() => !disabled && open?.()}
                        className={`p-4 border-4 cursor-pointer border-dashed border-primary rounded-lg hover:opacity-75 transition flex flex-col space-y-2 items-center justify-center ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
                    >
                        <div className="relative flex items-center justify-center lg:h-40 lg:w-96 mx-auto">
                            <div className="flex flex-col space-y-5 justify-center items-center w-full h-full">
                                <ImagePlus size={70} color="gray" />
                                <div className="font-semibold text-lg">
                                    点击上传图片(最多5张)
                                </div>
                            </div>
                        </div>
                    </div>
                )}
            </CldUploadWidget>

            {/* 展示已上传的所有图片 */}
            {value.length > 0 && (
                <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
                    {value.map((url, index) => (
                        <div key={index} className="relative group">
                            <Image
                                alt={`已上传图片 ${index + 1}`}
                                src={url}
                                width={200}
                                height={200}
                                className="rounded-lg object-cover w-full h-40"
                            />
                            <button
                                onClick={() => handleRemoveImage(index)}
                                className="absolute top-2 right-2 bg-red-500 text-white p-1 rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
                            >
                                <X size={16} />
                            </button>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};

export default ImageUpload;

二、父组件FormField配置调整

直接传递react-hook-form的field.onChange即可,它原生支持数组类型的更新:

// 父组件中images字段的FormField部分
<FormField
    control={form.control}
    name="images"
    render={({ field }) => (
        <FormItem className="flex flex-col space-y-5">
            <FormLabel className="font-bold text-base lg:text-3xl">
                上传图片
            </FormLabel>
            <FormControl>
                <ImageUpload
                    disabled={isLoading}
                    onChange={field.onChange}
                    value={field.value}
                />
            </FormControl>
            <FormDescription>
                上传项目相关图片,最多5张。
            </FormDescription>
            <FormMessage />
        </FormItem>
    )}
/>

三、关键修改说明

  • 数组化存储:将onChange参数改为数组类型,每次上传追加新URL而非覆盖。
  • 修复字段错误:修正Cloudinary返回的URL字段拼写(scure_url→secure_url)。
  • 交互优化:增加多图网格展示和单图移除功能,提升用户体验。
  • 禁用状态处理:表单提交中(isLoading)禁止上传操作,避免重复提交。

四、Zod校验保持原配置(可按需增强)

原有schema已支持数组类型,可根据需求添加数量约束:

const formSchema = z.object({
    title: z.string().min(5, {
        message: "Title must be at least 5 characters.",
    }),
    caption: z.string().min(10, {
        message: "Caption must be at least 10 characters.",
    }),
    // 可选:添加最少上传1张图片的约束
    images: z.array(z.string()).min(1, "请至少上传一张图片"),
});

内容的提问来源于stack exchange,提问作者Ismail Salah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:35:55