| 1 | /** |
| 2 | * MediaAddToReferenceAction - 图片资源追加到 AI 参考文件 |
| 3 | * 在纯图片资源卡片上提供一键追加到 AI 批量生成输入参考文件的入口 |
| 4 | */ |
| 5 | |
| 6 | 'use client' |
| 7 | |
| 8 | import type { MediaItem } from '@/api/materials/material.types' |
| 9 | import { ImagePlus } from 'lucide-react' |
| 10 | import { memo, useCallback } from 'react' |
| 11 | import { useTransClient } from '@/app/i18n/client' |
| 12 | import { Button } from '@/components/ui/button' |
| 13 | import { useDraftBoxConfigStore } from '@/store/draft-box/draftBoxConfigStore' |
| 14 | import { cn } from '@/utils/className' |
| 15 | import { getOssUrl } from '@/utils/oss' |
| 16 | import { toast } from '@/utils/ui/toast' |
| 17 | |
| 18 | interface MediaAddToReferenceActionProps { |
| 19 | media: MediaItem |
| 20 | groupId: string |
| 21 | maxImages: number |
| 22 | } |
| 23 | |
| 24 | export const MediaAddToReferenceAction = memo(({ |
| 25 | media, |
| 26 | groupId, |
| 27 | maxImages, |
| 28 | }: MediaAddToReferenceActionProps) => { |
| 29 | const { t } = useTransClient('brandPromotion') |
| 30 | |
| 31 | const handleAddToReference = useCallback(() => { |
| 32 | if (media.type !== 'img') { |
| 33 | return |
| 34 | } |
| 35 | |
| 36 | const { getConfig, appendPersistedMedias } = useDraftBoxConfigStore.getState() |
| 37 | const currentConfig = getConfig(groupId) |
| 38 | const persistedImageUrls = (currentConfig.persistedMedias ?? []) |
| 39 | .filter(item => item.type === 'image') |
| 40 | .map(item => getOssUrl(item.url)) |
| 41 | const currentImageCount = persistedImageUrls.length |
| 42 | const normalizedUrl = getOssUrl(media.url) |
| 43 | |
| 44 | if (persistedImageUrls.includes(normalizedUrl)) { |
| 45 | toast.info(t('detail.draftImagesAlreadyInReference')) |
| 46 | return |
| 47 | } |
| 48 | |
| 49 | const availableCount = Math.max(0, maxImages - currentImageCount) |
| 50 | if (availableCount === 0) { |
| 51 | toast.warning(t('detail.imageCountExceeded', { max: maxImages })) |
| 52 | return |
| 53 | } |
| 54 | |
| 55 | const { added } = appendPersistedMedias(groupId, [{ |
| 56 | id: `media-reference-${media._id}`, |
| 57 | url: normalizedUrl, |
| 58 | type: 'image', |
| 59 | name: media.title || undefined, |
| 60 | }]) |
| 61 | |
| 62 | if (added === 0) { |
| 63 | toast.info(t('detail.draftImagesAlreadyInReference')) |
| 64 | return |
| 65 | } |
| 66 | |
| 67 | toast.success(t('detail.draftImagesAddedToReference', { count: added })) |
| 68 | }, [groupId, maxImages, media, t]) |
| 69 | |
| 70 | if (media.type !== 'img') { |
| 71 | return null |
| 72 | } |
| 73 | |
| 74 | return ( |
| 75 | <Button |
| 76 | type="button" |
| 77 | onClick={handleAddToReference} |
| 78 | className={cn( |
| 79 | 'h-9 rounded-full border border-primary/25 bg-background/95 px-3 text-xs font-semibold text-foreground shadow-lg shadow-primary/15 backdrop-blur-md', |
| 80 | 'cursor-pointer gap-1.5 transition-all duration-200 hover:border-primary/40 hover:bg-background hover:shadow-xl hover:shadow-primary/20', |
| 81 | )} |
| 82 | > |
| 83 | <ImagePlus className="h-4 w-4 text-primary" /> |
| 84 | <span>{t('detail.addDraftImagesToReference')}</span> |
| 85 | </Button> |
| 86 | ) |
| 87 | }) |
| 88 | |
| 89 | MediaAddToReferenceAction.displayName = 'MediaAddToReferenceAction' |
| 90 |