Files
test1/components/nova-sdk/message-list/message-item/ImageAttachmentItem.tsx
2026-03-20 07:33:46 +00:00

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>
)
})