diff --git a/app/api/open/get-image-blob/route.ts b/app/api/open/get-image-blob/route.ts new file mode 100644 index 0000000..f96c768 --- /dev/null +++ b/app/api/open/get-image-blob/route.ts @@ -0,0 +1,12 @@ +import 'server-only' +import { NextRequest } from 'next/server' + +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url) + const imageUrl = searchParams.get('imageUrl') + // @ts-ignore + const blob = await fetch(imageUrl).then(res => res.blob()) + return new Response(blob) +} + +export const revalidate = 0 \ No newline at end of file diff --git a/components/BlurImage.tsx b/components/BlurImage.tsx index 933d1a5..4bf2fa2 100644 --- a/components/BlurImage.tsx +++ b/components/BlurImage.tsx @@ -1,12 +1,10 @@ 'use client' -import React, { useState } from 'react' +import React from 'react' import { useButtonStore } from '~/app/providers/button-store-Providers' import { Image } from '@nextui-org/image' -import { cn } from '~/utils' export default function BlurImage({ photo }: { photo: any }) { - const [isLoading, setIsLoading] = useState(true) const { setMasonryView, setMasonryViewData } = useButtonStore( (state) => state, ) @@ -26,13 +24,7 @@ export default function BlurImage({ photo }: { photo: any }) { setMasonryView(true) setMasonryViewData(photo) }} - className={cn( - 'duration-700 ease-in-out group-hover:opacity-75 cursor-pointer transition-all will-change-transform hover:scale-[1.01]', - isLoading - ? 'scale-110 blur-2xl grayscale opacity-50' - : 'scale-100 blur-0 grayscale-0' - )} - onLoad={() => setIsLoading(false)} + className="duration-700 ease-in-out group-hover:opacity-75 cursor-pointer transition-all will-change-transform hover:scale-[1.01]" /> ) diff --git a/components/MasonryItem.tsx b/components/MasonryItem.tsx index 31f084c..f3f535c 100644 --- a/components/MasonryItem.tsx +++ b/components/MasonryItem.tsx @@ -6,14 +6,15 @@ import { } from '~/components/ui/Dialog' import { useButtonStore } from '~/app/providers/button-store-Providers' import { CopyrightType, DataProps, ImageType } from '~/types' -import { Image, Tabs, Tab, Card, CardHeader, CardBody, CardFooter, Button, Chip, Link, Avatar } from '@nextui-org/react' -import { Aperture, Camera, Image as ImageIcon, Languages, CalendarDays, X, SunMedium, MoonStar, Copyright, Crosshair, Timer, CircleGauge, Copy, Share2 } from 'lucide-react' +import { Image, Tabs, Tab, Card, CardHeader, CardBody, CardFooter, Button, Chip, Link, Avatar, Tooltip } from '@nextui-org/react' +import { Aperture, Camera, Image as ImageIcon, Images, Link as LinkIcon, ImageDown, Languages, CalendarDays, X, SunMedium, MoonStar, Copyright, Crosshair, Timer, CircleGauge, Share2 } from 'lucide-react' import * as React from 'react' import { useTheme } from 'next-themes' import { useRouter } from 'next-nprogress-bar' import ExifView from '~/components/ExifView' import { toast } from 'sonner' import { usePathname } from 'next/navigation' +import useSWR from 'swr' export default function MasonryItem() { const router = useRouter() @@ -21,6 +22,7 @@ export default function MasonryItem() { const { MasonryView, MasonryViewData, setMasonryView, setMasonryViewData } = useButtonStore( (state) => state, ) + const {data: download = false, mutate: setDownload} = useSWR(['masonry/download', MasonryViewData.url], null) const { theme, setTheme } = useTheme() const props: DataProps = { @@ -44,6 +46,30 @@ export default function MasonryItem() { } } + async function downloadImg() { + setDownload(true) + try { + toast.warning('开始下载,原图较大,请耐心等待!', { duration: 1500 }) + await fetch(`/api/open/get-image-blob?imageUrl=${MasonryViewData.url}`) + .then((response) => response.blob()) + .then((blob) => { + const url = window.URL.createObjectURL(new Blob([blob])); + const link = document.createElement("a"); + link.href = url; + const parsedUrl = new URL(MasonryViewData.url); + const filename = parsedUrl.pathname.split('/').pop(); + link.download = filename || "downloaded-file"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }) + } catch (e) { + toast.error('下载失败!', { duration: 500 }) + } finally { + setDownload(false) + } + } + return (
-
+

{MasonryViewData.title}

{ navigator.canShare && typeof navigator.canShare === 'function' && + + + + } + - } - - + + + +
{MasonryViewData?.exif?.model && MasonryViewData?.exif?.f_number && MasonryViewData?.exif?.exposure_time && MasonryViewData?.exif?.focal_length && MasonryViewData?.exif?.iso_speed_rating && diff --git a/components/admin/list/ImageView.tsx b/components/admin/list/ImageView.tsx index 2ae623e..827437c 100644 --- a/components/admin/list/ImageView.tsx +++ b/components/admin/list/ImageView.tsx @@ -47,7 +47,7 @@ export default function ImageView() { {imageViewData.detail} {imageViewData?.labels && diff --git a/components/admin/list/ListImage.tsx b/components/admin/list/ListImage.tsx index aaeef0e..ca6a822 100644 --- a/components/admin/list/ListImage.tsx +++ b/components/admin/list/ListImage.tsx @@ -1,12 +1,9 @@ 'use client' -import React, { useState } from 'react' +import React from 'react' import { Image } from '@nextui-org/image' -import { cn } from '~/utils' export default function ListImage({ image }: { image: any }) { - const [isLoading, setIsLoading] = useState(true) - return ( setIsLoading(false)} + className="duration-700 ease-in-out group-hover:opacity-75 object-cover" /> ) } \ No newline at end of file