import { useEffect, useState } from 'react' import { Check, CheckSquare, Film, FolderOpen, HeartHandshake, Layers, Loader2, Music, Save, SlidersHorizontal, Square, Tv, } from 'lucide-react' import toast from 'react-hot-toast' import { adminAPI } from '../api/admin' import { imageURL } from '../api/client' import { libraryAPI } from '../api/library' import type { Library, Setting } from '../types' import { groupSeries, type SeriesCard } from '../utils/groupSeries' import { getLibraryArtworks } from './librariesPageModel' const CAROUSEL_STORAGE_KEY = 'mmtl.home.carousel_libraries' const SETTING_KEY_CAROUSEL = 'home.carousel_libraries' const TYPE_ICONS: Record = { movie: , movies: , tv: , series: , anime: , shows: , variety: , music: , adult: , } const TYPE_LABELS: Record = { movie: '电影', movies: '电影', tv: '剧集', series: '剧集', anime: '动漫', shows: '综艺', variety: '综艺', music: '音乐', adult: 'Adult', } export function LibrarySettingsPanel() { const [libraries, setLibraries] = useState([]) const [libraryCards, setLibraryCards] = useState>({}) const [selectedIds, setSelectedIds] = useState([]) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [dirty, setDirty] = useState(false) useEffect(() => { async function load() { setLoading(true) try { const [libs, settings] = await Promise.all([ libraryAPI.list({ includeHidden: true }).catch(() => [] as Library[]), adminAPI.listSettings().catch(() => [] as Setting[]), ]) const libList = Array.isArray(libs) ? libs : [] setLibraries(libList) // 异步拉取各个媒体库的前几个条目用于封面展示 Promise.allSettled( libList.map(async (lib) => { const page = await libraryAPI.listMedia(lib.id, 1, 10) const items = Array.isArray(page?.items) ? page.items : [] return { id: lib.id, cards: groupSeries(items) } }), ).then((results) => { const map: Record = {} for (const r of results) { if (r.status === 'fulfilled' && r.value) { map[r.value.id] = r.value.cards } } setLibraryCards(map) }) // 优先读取系统配置,其次读取 localStorage,默认全选 const settingItem = Array.isArray(settings) ? settings.find((s) => s.key === SETTING_KEY_CAROUSEL) : undefined let initialIds: string[] | null = null if (settingItem?.value) { try { initialIds = JSON.parse(settingItem.value) } catch { // ignore } } if (!initialIds) { try { const saved = localStorage.getItem(CAROUSEL_STORAGE_KEY) if (saved) initialIds = JSON.parse(saved) } catch { // ignore } } if (Array.isArray(initialIds)) { setSelectedIds(initialIds) } else { setSelectedIds(libList.map((l) => l.id)) } } finally { setLoading(false) } } load() }, []) const toggleLibrary = (id: string) => { setSelectedIds((prev) => { const next = prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] setDirty(true) return next }) } const selectAll = () => { setSelectedIds(libraries.map((l) => l.id)) setDirty(true) } const deselectAll = () => { setSelectedIds([]) setDirty(true) } const handleSave = async () => { setSaving(true) try { const jsonValue = JSON.stringify(selectedIds) await adminAPI.updateSetting(SETTING_KEY_CAROUSEL, jsonValue) try { localStorage.setItem(CAROUSEL_STORAGE_KEY, jsonValue) } catch { // ignore } toast.success('海报轮播设置已保存') setDirty(false) } catch (err: unknown) { const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? '保存设置失败' toast.error(msg) } finally { setSaving(false) } } if (loading) { return (
) } return (
{/* 头部说明与快捷操作 */}

首页海报轮播设置

选择参与首页顶部大图海报轮播推荐的媒体库。勾选的媒体库内容将轮流展示在系统首页顶部。

{/* 媒体库列表卡片 */} {libraries.length === 0 ? (
暂无可用媒体库,请先添加媒体库后再配置海报轮播。
) : (
{libraries.map((lib) => { const isSelected = selectedIds.includes(lib.id) const cards = libraryCards[lib.id] || [] const artwork = getLibraryArtworks(lib, cards) return (
toggleLibrary(lib.id)} className={`flex cursor-pointer items-center justify-between rounded-2xl border p-4 transition-all duration-200 select-none ${ isSelected ? 'border-brand-500/60 bg-primary-400/10 shadow-sm' : 'border-gray-200 bg-white/70 hover:border-gray-300' }`} >
1 ? 'grid-cols-2' : 'grid-cols-1' } ${ isSelected ? 'ring-2 ring-brand-500/30' : '' }`} > {artwork.length > 0 ? ( artwork.map(({ src, version }, index) => ( { e.currentTarget.style.display = 'none' }} /> )) ) : (
{TYPE_ICONS[lib.type] || }
)}
{lib.name} {TYPE_LABELS[lib.type] || '自定义'}

{isSelected ? '已启用轮播' : '未参与轮播'}

) })}
)} {/* 底部保存按钮 */}
已选择 {selectedIds.length} / {libraries.length} 个媒体库参与轮播
) }