"use client"; import { Copy, FolderPlus, Search } from "lucide-react"; import { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { App, Button, Card, Drawer, Empty, Image, Input, Pagination, Spin, Tag, Typography } from "antd"; import axios from "axios"; import copy from "copy-to-clipboard"; import { cn } from "@/lib/utils"; import { useAssetStore } from "@/stores/use-asset-store"; import { fetchAssetLibrary, type AssetLibraryItem } from "@/services/api/assets"; import { uploadImage } from "@/services/image-storage"; const PAGE_SIZE = 12; export default function AssetLibraryPage() { const { message } = App.useApp(); const [keyword, setKeyword] = useState(""); const [selectedType, setSelectedType] = useState(""); const [selectedTags, setSelectedTags] = useState([]); const [page, setPage] = useState(1); const [selectedAsset, setSelectedAsset] = useState(null); const addAsset = useAssetStore((state) => state.addAsset); const query = useQuery({ queryKey: ["asset-library", keyword, selectedType, selectedTags, page], queryFn: () => fetchAssetLibrary({ keyword, type: selectedType, tag: selectedTags, page, pageSize: PAGE_SIZE }), retry: false, }); useEffect(() => { if (query.isError) { message.error(query.error instanceof Error ? query.error.message : "获取素材库失败"); } }, [message, query.error, query.isError]); const isReady = query.isFetched || query.isError; const items = query.data?.items || []; const availableTags = query.data?.tags || []; const total = query.data?.total || 0; const toggleTag = (tag: string) => { setSelectedTags((items) => items.includes(tag) ? items.filter((item) => item !== tag) : [...items, tag]); }; const saveToMyAssets = async (asset: AssetLibraryItem) => { try { if (asset.type === "image") { const dataUrl = await remoteImageToDataUrl(asset.url); const image = await uploadImage(dataUrl); addAsset({ kind: "image", title: asset.title, coverUrl: asset.coverUrl, tags: asset.tags, source: asset.category, note: asset.description, data: { dataUrl: image.url, storageKey: image.storageKey, width: image.width, height: image.height, bytes: image.bytes, mimeType: image.mimeType }, metadata: { source: "asset-library", assetId: asset.id }, }); } else { addAsset({ kind: "text", title: asset.title, coverUrl: asset.coverUrl, tags: asset.tags, source: asset.category, note: asset.description, data: { content: asset.content }, metadata: { source: "asset-library", assetId: asset.id }, }); } message.success("已加入我的素材"); } catch { message.error("加入失败"); } }; const copyText = async (value: string) => { copy(value); message.success("已复制"); }; if (!isReady) { return (
); } return (

素材库

挑选团队素材,加入我的素材后继续编辑和使用。

} value={keyword} placeholder="按标题查询" onChange={(event) => { setPage(1); setKeyword(event.target.value); }} />
类型
{[ { label: "全部", value: "" }, { label: "文本", value: "text" }, { label: "图片", value: "image" }, ].map((item) => ( { setPage(1); setSelectedType(item.value); }}> {item.label} ))}
标签
{ setPage(1); setSelectedTags([]); }}> 全部 {availableTags.map((tag) => ( { setPage(1); toggleTag(tag); }}> {tag} ))}
{items.map((asset) => ( setSelectedAsset(asset)} onAdd={() => void saveToMyAssets(asset)} /> ))}
{!items.length ? : null}
setPage(nextPage)} />
setSelectedAsset(null)}> {selectedAsset ? (
{selectedAsset.coverUrl ? {selectedAsset.title} :
{selectedAsset.content || "暂无封面"}
}
{selectedAsset.title}
{selectedAsset.type === "image" ? "图片" : "文本"} {selectedAsset.tags.map((tag) => {tag})}
内容 {selectedAsset.type === "text" ? {selectedAsset.content} : {selectedAsset.url}}
{selectedAsset.description ? {selectedAsset.description} : null}
{selectedAsset.type === "text" ? : null} {selectedAsset.type === "image" ? : null}
) : null}
); } function LibraryCard({ asset, onOpen, onAdd }: { asset: AssetLibraryItem; onOpen: () => void; onAdd: () => void }) { const cover = asset.coverUrl; return ( {cover ? {asset.title} :
{asset.content || "暂无封面"}
}}>
); } async function remoteImageToDataUrl(url: string) { const response = await axios.get(url, { responseType: "blob" }); const blob = response.data as Blob; return await blobToDataUrl(blob); } function blobToDataUrl(blob: Blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(String(reader.result || "")); reader.onerror = () => reject(new Error("读取图片失败")); reader.readAsDataURL(blob); }); }