'use client' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import Image from 'next/image' import Link from 'next/link' import { BookOpen, ChevronDown, Download, FileText, Heart, Search, Share2, Upload } from 'lucide-react' import DepartmentShell from './DepartmentShell' import BrandKnowledgeUploadDialog from './BrandKnowledgeUploadDialog' import { BRAND_KNOWLEDGE_TYPE_OPTIONS } from '@/lib/brand-knowledge-file-types' const KNOWLEDGE_TYPES = ['全部', ...BRAND_KNOWLEDGE_TYPE_OPTIONS] type BrandKnowledgeItem = { id: string; title: string; knowledgeType: string; brand: string; productCategoryName: string; fileName: string; mimeType: string; fileSize: number; downloadCount: number; createdAt: string; uploadedBy: string; downloadUrl: string; previewUrl: string | null; previewImageName: string | null; likeCount: number; isLiked: boolean; isFavorited: boolean } function formatFileSize(size: number) { if (size < 1024) return `${size} B`; if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB`; if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)} MB`; return `${(size / (1024 * 1024 * 1024)).toFixed(2)} GB` } function formatDate(value: string) { const date = new Date(value); if (Number.isNaN(date.getTime())) return value; const parts = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date); const values = Object.fromEntries(parts.map((part) => [part.type, part.value])); return `${values.year}.${values.month}.${values.day}` } async function copyText(value: string) { try { await navigator.clipboard.writeText(value) return true } catch { const textarea = document.createElement('textarea') textarea.value = value textarea.style.position = 'fixed' textarea.style.left = '-999999px' document.body.appendChild(textarea) textarea.select() try { return document.execCommand('copy') } catch { return false } finally { document.body.removeChild(textarea) } } } function BrandKnowledgeCard({ item, onNotice, onLike, onFavorite }: { item: BrandKnowledgeItem onNotice: (message: string) => void onLike: (id: string, active: boolean) => Promise onFavorite: (id: string, active: boolean) => Promise }) { const [busyAction, setBusyAction] = useState<'like' | 'favorite' | null>(null) const details = [ formatFileSize(item.fileSize), item.uploadedBy, formatDate(item.createdAt), item.knowledgeType, item.brand && item.brand !== '未关联品牌' ? item.brand : '', item.productCategoryName, ].filter(Boolean) const handleCopyId = async () => { onNotice(await copyText(item.id) ? '作品ID已复制' : '作品ID复制失败,请手动复制') } const handleShare = async () => { const url = new URL(`/workspaces/assets/brand-knowledge/${encodeURIComponent(item.id)}`, window.location.origin) onNotice(await copyText(url.toString()) ? '分享链接已复制' : '分享链接复制失败,请手动复制') } const handleEngagement = async (action: 'like' | 'favorite') => { if (busyAction) return setBusyAction(action) try { if (action === 'like') { await onLike(item.id, !item.isLiked) onNotice(item.isLiked ? '已取消点赞' : '点赞成功') } else { await onFavorite(item.id, !item.isFavorited) onNotice(item.isFavorited ? '已取消收藏' : '收藏成功') } } catch (error) { onNotice(error instanceof Error ? error.message : '操作失败,请稍后重试') } finally { setBusyAction(null) } } return (
  • {item.previewUrl ? ( {`预览图:${item.title}`} ) : (
    )}

    {item.title}

    {details.map((detail, index) => ( {index > 0 && } {detail} ))}

  • ) } export default function BrandKnowledgeDashboard() { const [selectedType, setSelectedType] = useState('全部') const [searchKeyword, setSearchKeyword] = useState('') const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false) const [items, setItems] = useState([]) const [isLoading, setIsLoading] = useState(true) const [loadError, setLoadError] = useState('') const [notice, setNotice] = useState('') const uploadButtonRef = useRef(null) const loadKnowledge = useCallback(async () => { setIsLoading(true); setLoadError('') try { const response = await fetch('/api/department-dashboard/brand-knowledge', { cache: 'no-store' }); const payload = await response.json().catch(() => null); if (!response.ok || !payload?.success) throw new Error(payload?.error || '品牌知识列表读取失败'); setItems(Array.isArray(payload.data) ? payload.data : []) } catch (error) { setLoadError(error instanceof Error ? error.message : '品牌知识列表读取失败,请稍后重试') } finally { setIsLoading(false) } }, []) useEffect(() => { void loadKnowledge() }, [loadKnowledge]) useEffect(() => { const sharedKnowledgeId = new URLSearchParams(window.location.search).get('knowledge')?.trim() if (sharedKnowledgeId) setSearchKeyword(sharedKnowledgeId) }, []) const setEngagement = useCallback(async (id: string, action: 'like' | 'favorite', active: boolean) => { const response = await fetch(`/api/department-dashboard/brand-knowledge/${encodeURIComponent(id)}/${action}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ active }), }) const payload = await response.json().catch(() => null) if (!response.ok || !payload?.success) throw new Error(payload?.error || '操作失败,请稍后重试') setItems((current) => current.map((item) => item.id === id ? { ...item, ...(action === 'like' ? { isLiked: payload.liked, likeCount: payload.likeCount } : { isFavorited: payload.isFavorited }), } : item)) }, []) const visibleItems = useMemo(() => { const keyword = searchKeyword.trim().toLowerCase(); return items.filter((item) => (selectedType === '全部' || item.knowledgeType === selectedType) && (!keyword || [item.id, item.title, item.fileName, item.brand, item.productCategoryName, item.uploadedBy].some((value) => value.toLowerCase().includes(keyword)))) }, [items, searchKeyword, selectedType]) const emptyMessage = useMemo(() => { if (loadError) return loadError if (searchKeyword.trim()) return `没有找到与“${searchKeyword.trim()}”匹配的知识内容` if (selectedType !== '全部') return `${selectedType}暂未上传内容` return '品牌知识库暂未上传内容' }, [loadError, searchKeyword, selectedType]) return (

    品牌知识库

    品牌中心部门工作台

    {notice}

    知识内容

    人员上传的知识文件将按类型保存,并可在此下载

    {isLoading ?
    正在读取知识内容…
    : visibleItems.length > 0 ?
      {visibleItems.map((item) => setEngagement(id, 'like', active)} onFavorite={(id, active) => setEngagement(id, 'favorite', active)} />)}
    :
    }
    {isUploadDialogOpen && ( setIsUploadDialogOpen(false)} onUploaded={async () => { setNotice('知识文件已上传到品牌知识库'); await loadKnowledge() }} returnFocusRef={uploadButtonRef} /> )}
    ) }