无法在cloudinary上传多张图片

问题描述 投票:0回答:1

我想创建一个产品页面,接下来我可以使用 Cloudinary 上传多个图像。这里我创建了一个上传图片的组件

图片上传组件

"use client";

import { CldUploadWidget } from 'next-cloudinary';
import { useEffect, useState } from 'react';

import { Button } from '@/components/ui/button';
import Image from 'next/image';
import { ImagePlus, Trash } from 'lucide-react';

interface ImageUploadProps {
  disabled?: boolean;
  onChange: (value: string) => void;
  onRemove: (value: string) => void;
  value: string[];
}

const ImageUpload: React.FC<ImageUploadProps> = ({
  disabled,
  onChange,
  onRemove,
  value
}) => {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  
  const onUpload = (result: any) => {
    onChange(result.info.secure_url);
  };
  

  if (!isMounted) {
    return null;
  }

  return ( 
    <div>
      <div className="mb-4 flex items-center gap-4">
        {value.map((url) => (
          <div key={url} className="relative w-[200px] h-[200px] rounded-md overflow-hidden">
            <div className="z-10 absolute top-2 right-2">
              <Button type="button" onClick={() => onRemove(url)} variant="destructive" size="sm">
                <Trash className="h-4 w-4" />
              </Button>
            </div>
            <Image
              fill
              sizes=''
              className="object-cover"
              alt="Image"
              src={url}
            />
          </div>
          
        ))
        }
      
      </div>
      <CldUploadWidget onSuccess={onUpload} uploadPreset="ox48luzl">
        {({ open }) => {
          const onClick = () => {
            open();
          };

          return (
            <Button 
              type="button" 
              disabled={disabled} 
              variant="secondary" 
              onClick={onClick}
            >
              <ImagePlus className="h-4 w-4 mr-2" />
              Upload an Image
            </Button>
          );
        }}
      </CldUploadWidget>
    </div>
  );
}
 
export default ImageUpload;

产品表格页面

我称之为图像上传

'use client'

import { Button } from "@/components/ui/button"
import { Heading } from "@/components/ui/heading"
import { Product, Image, Category } from "@prisma/client";
import { Trash } from "lucide-react"
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form";
import * as z from "zod"
import { AlertModal } from "@/components/modals/alert-model";
import axios from "axios";
import toast from "react-hot-toast";
import { Separator } from "@/components/ui/separator";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import ImageUpload from "@/components/ui/image-upload";


const formSchema = z.object({
  name: z.string().min(1),
  promocode: z.string().min(2),
  affiliateLink: z.string().min(1),
  description: z.string().min(1),
  images:z.object({url:z.string()}).array(),
  categoryId: z.string().min(1),
  price: z.coerce.number().min(1),
})

type ProductFormValues = z.infer<typeof formSchema>;

interface ProductFormProps {
  initialData: Product & {
    images: Image[]
  } | null;
  categories: Category[]
};
const ProductForm: React.FC<ProductFormProps> = ({
  initialData,
  categories
}) => {

  const params = useParams();
  const router = useRouter();

  const [open, setOpen] = useState(false);
  const [loading, setLoading] = useState(false);

  const title = initialData ? 'Edit Product' : 'Create Product'
  const description = initialData ? 'Edit a Product' : 'Add a new Product';
  const toastMassege = initialData ? 'Product Update' : 'Product Created';
  const action = initialData ? 'Save Changes' : 'Create';

  const defaultValues  = initialData ? {
    ...initialData,
    price: parseFloat(String(initialData?.price)),
    promocode: initialData.promocode || "", 
  } : {
    name: '',
    images:[],
    price:0,
    description: '',
    catogoryId: '',
    promocode: '',
    affiliateLink: '',
  }

  const form = useForm<ProductFormValues>({
    resolver: zodResolver(formSchema),defaultValues
  })

  const onDelete = async () => {
    try {
      setLoading(true)
      await axios.delete(`/api/products/${params.productId}`)
      router.push('/products')
      toast.success('Product Deleted Successfully!')
    } catch (error: any) {
      toast.error('something wen wrong')
    }
    finally {
      setLoading(false)
    }
  }
  return (
    <>
      <AlertModal
        isOpen={open}
        onClose={() => setOpen(true)}
        onConfirm={onDelete}
        loading={loading}
      />
      <div className="flex item-center justify-between">
        <Heading title={title} description={description} />
        {initialData &&(
          <Button
          disabled={loading}
         variant="destructive"
         size="sm"
       >
         <Trash className="h-4 w-4" />
       </Button>
        )}
      </div>
      <Separator/>
      <Form {...form}>
          <form className="space-y-8 w-full">
          <FormField
            control={form.control}
            name="images"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Images</FormLabel>
                <FormControl>
                  <ImageUpload 
                    value={field.value.map((image)=>image.url)} 
                    disabled={loading} 
                    onChange={(url) => field.onChange([...field.value, { url }])}
                    onRemove={(url) => field.onChange([...field.value.filter((current) => current.url !== url)])}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          /> 
            <FormField 
            control={form.control} 
            name="name"
            render={({field})=>(
              <FormItem>
                <FormLabel>Product Name</FormLabel>
                <FormControl>
                  <Input disabled={loading} placeholder="Enter Product Name" {...field}/>
                </FormControl>
              </FormItem>
            )} />
          
          </form>
      </Form>
    </>
  )
}

export default ProductForm

在这段代码中,几乎一切都工作正常,但是当尝试在 Cloudinary 小部件中上传多个图像时,只有第一个或第一个上传的图像显示并存储在值中。

IU 想实现一组上传和存储的图像 URL。

reactjs typescript next.js next.js13 cloudinary
1个回答
0
投票

您需要将

multiple
参数添加到上传小部件。

您可以通过文档阅读: https://cloudinary.com/documentation/upload_widget_reference#:~:text=opened.%0ADefault%3A%20local-,multiple,-Boolean

© www.soinside.com 2019 - 2024. All rights reserved.