102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import { useState, useEffect, memo } from 'react'
|
|
import { Loader2 } from 'lucide-react'
|
|
import { Image } from '@/components/ui/image'
|
|
import { ImagePreview } from '@/components/ui/image-preview'
|
|
import type { HandleImageAttachmentClick, ImageAttachment, TaskArtifact } from '../../types'
|
|
import { useNovaKit } from '../../context/useNovaKit'
|
|
|
|
export interface ImageAttachmentItemProps {
|
|
image: ImageAttachment
|
|
assetsType: 'assistant' | 'user'
|
|
onClick?: HandleImageAttachmentClick
|
|
}
|
|
|
|
/**
|
|
* 判断是否需要加载 OSS URL
|
|
*/
|
|
function needsOssUrl(image: ImageAttachment): boolean {
|
|
if (image.url && image.url.startsWith('http')) {
|
|
return false
|
|
}
|
|
return !!(image.path || image.url)
|
|
}
|
|
|
|
/**
|
|
* 图片附件展示组件
|
|
*/
|
|
export const ImageAttachmentItem = memo(function ImageAttachmentItem({
|
|
image,
|
|
assetsType,
|
|
onClick,
|
|
}: ImageAttachmentItemProps) {
|
|
const { api } = useNovaKit()
|
|
const initialUrl = image.url?.startsWith('http') ? image.url : ''
|
|
const [url, setUrl] = useState<string>(initialUrl)
|
|
const [loading, setLoading] = useState(needsOssUrl(image))
|
|
|
|
useEffect(() => {
|
|
if (image.url && image.url.startsWith('http')) {
|
|
return
|
|
}
|
|
|
|
const filePath = image.path || image.url || image.file_url
|
|
if (filePath) {
|
|
api
|
|
.getArtifactUrl?.({ ...image, path: filePath } as TaskArtifact)
|
|
.then((res: { data: string }) => {
|
|
const fileUrls = res?.data
|
|
if (fileUrls) {
|
|
setUrl(fileUrls)
|
|
image.url = fileUrls
|
|
}
|
|
})
|
|
.catch((err: Error) => {
|
|
console.error('获取图片 URL 失败:', err)
|
|
})
|
|
.finally(() => {
|
|
setLoading(false)
|
|
})
|
|
}
|
|
}, [image, image.url, image.path, api])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-w-32 h-full min-h-32 rounded-lg">
|
|
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!url) return null
|
|
|
|
const handleClick = () => {
|
|
if (onClick) {
|
|
onClick({ ...image, url: url || image.url }, assetsType)
|
|
}
|
|
}
|
|
|
|
if (onClick) {
|
|
return (
|
|
<div onClick={handleClick} className="cursor-pointer">
|
|
<Image
|
|
src={url}
|
|
alt={image.file_name || '图片'}
|
|
className="max-w-full max-h-[300px] rounded-lg hover:opacity-80 transition-opacity"
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center justify-center w-full h-full">
|
|
<ImagePreview src={url} alt={image.file_name || '图片'}>
|
|
<Image
|
|
src={url}
|
|
alt={image.file_name || '图片'}
|
|
className="max-w-full max-h-[300px] rounded-lg hover:opacity-80 transition-opacity"
|
|
/>
|
|
</ImagePreview>
|
|
</div>
|
|
)
|
|
})
|