"use client" import { useState, useEffect, useCallback, use } from "react" import { ChevronLeft, Download, Plus, Search, Tag, Trash2, BarChart, RefreshCw, Image as ImageIcon, Edit } from "lucide-react" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { useRouter } from "next/navigation" import { toast } from "@/components/ui/use-toast" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { api } from "@/lib/api" import { showToast } from "@/lib/toast" import { Avatar } from "@/components/ui/avatar" import { Separator } from "@/components/ui/separator" import { format } from "date-fns" import Image from "next/image" import { cn } from "@/lib/utils" interface ApiResponse { code: number msg: string data: T } interface MaterialListResponse { list: Material[] total: number } interface Material { id: number type: string title: string content: string coverImage: string | null resUrls: string[] urls: string[] createTime: string createMomentTime: number time: string wechatId: string friendId: string | null wechatChatroomId: number senderNickname: string senderAvatar: string // 发布朋友圈用户的头像 location: string | null lat: string lng: string } const isImageUrl = (url: string) => { return /\.(jpg|jpeg|png|gif|webp)$/i.test(url) || url.includes('oss-cn-shenzhen.aliyuncs.com') } export default function MaterialsPage({ params }: { params: Promise<{ id: string }> }) { const resolvedParams = use(params) const router = useRouter() const [materials, setMaterials] = useState([]) const [searchQuery, setSearchQuery] = useState("") const [isLoading, setIsLoading] = useState(true) const [selectedMaterial, setSelectedMaterial] = useState(null) const [page, setPage] = useState(1) const [total, setTotal] = useState(0) const limit = 20 const [deleteDialogOpen, setDeleteDialogOpen] = useState(null) const fetchMaterials = useCallback(async () => { setIsLoading(true) try { const queryParams = new URLSearchParams({ page: page.toString(), limit: limit.toString(), libraryId: resolvedParams.id, ...(searchQuery ? { keyword: searchQuery } : {}) }) const response = await api.get>(`/v1/content/library/item-list?${queryParams.toString()}`) if (response.code === 200 && response.data) { setMaterials(response.data.list) setTotal(response.data.total) } else { showToast(response.msg || "获取素材数据失败", "error") } } catch (error: any) { console.error("Failed to fetch materials:", error) showToast(error?.message || "请检查网络连接", "error") } finally { setIsLoading(false) } }, [page, searchQuery, resolvedParams.id]) useEffect(() => { fetchMaterials() }, [fetchMaterials]) const handleDownload = () => { showToast("正在将素材导出为Excel格式", "loading") } const handleNewMaterial = () => { router.push(`/content/${resolvedParams.id}/materials/new`) } const handleAIAnalysis = async (material: Material) => { try { // 模拟AI分析过程 await new Promise((resolve) => setTimeout(resolve, 1000)) const analysis = "这是一条" + material.title + "相关的内容,情感倾向积极。" setSelectedMaterial(material) showToast("AI分析完成", "success") } catch (error) { console.error("AI analysis failed:", error) showToast("AI分析失败", "error") } } const handleSearch = () => { setPage(1) fetchMaterials() } const handleRefresh = () => { fetchMaterials() } const handleDelete = async (id: number) => { const loadingToast = showToast("正在删除...", "loading", true) try { const response = await api.delete(`/v1/content/library/delete-item?id=${id}`) if (response.code === 200) { showToast("删除成功", "success") fetchMaterials() } else { showToast(response.msg || "删除失败", "error") } } catch (error: any) { showToast(error?.message || "删除失败", "error") } finally { loadingToast.remove && loadingToast.remove() setDeleteDialogOpen(null) } } // 处理内容显示 const renderContent = (material: Material) => { // 如果内容是图片,由renderImageResources处理 if (isImageUrl(material.content)) { return null } // 显示文本内容 return (

{material.content}

) } // 处理图片资源 const renderImageResources = (material: Material) => { const imageUrls = material.resUrls.filter(isImageUrl) // 如果内容本身是图片,也添加到图片数组中 if (isImageUrl(material.content) && !imageUrls.includes(material.content)) { imageUrls.unshift(material.content) } if (imageUrls.length === 0) return null // 微信朋友圈风格的图片布局 if (imageUrls.length === 1) { // 单张图片:大图显示 return (
图片内容
) } else if (imageUrls.length === 2) { // 两张图片:横向排列 return (
{imageUrls.map((url, idx) => (
{`图片
))}
) } else if (imageUrls.length === 3) { // 三张图片:使用3x3网格的前三个格子 return (
{imageUrls.map((url, idx) => (
{`图片
))}
) } else if (imageUrls.length === 4) { // 四张图片:2x2网格 return (
{imageUrls.map((url, idx) => (
{`图片
))}
) } else { // 五张及以上:3x3网格 const displayImages = imageUrls.slice(0, 9) const hasMore = imageUrls.length > 9 return (
{displayImages.map((url, idx) => (
{`图片 {idx === 8 && hasMore && (
+{imageUrls.length - 9}
)}
))}
) } } const handleSubmit = async () => { fetchMaterials() } return (

已采集素材

setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} />
{isLoading ? ( // 加载状态
{Array.from({ length: 3 }).map((_, index) => (
))}
) : ( // 素材列表
{materials.length === 0 ? ( 暂无素材数据 ) : ( materials.map(material => (
{material.senderNickname}
{material.senderNickname}
{material.time && format(new Date(material.time), 'yyyy-MM-dd HH:mm')}
ID: {material.id}
{/* 文本内容 */} {renderContent(material)} {/* 图片资源 */} {renderImageResources(material)} {/* 非图片资源标签 */} {material.resUrls.length > 0 && !material.resUrls.some(isImageUrl) && (
{material.resUrls.map((url, index) => ( 资源 {index + 1} ))}
)}
AI 分析结果

正在分析中...

setDeleteDialogOpen(open ? material.id : null)}> 确认删除
确定要删除该素材吗?此操作不可恢复。
)) )}
)} {!isLoading && total > limit && (
第 {page} 页,共 {Math.ceil(total / limit)} 页
)}
) }