mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-03-06 20:17:32 +08:00
feat: douban page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Film, Folder, Home, Menu, Search, Star, Tv } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
@@ -26,7 +26,7 @@ const Logo = () => (
|
||||
className='flex items-center justify-center h-16 select-none hover:opacity-80 transition-opacity duration-200'
|
||||
>
|
||||
<span className='text-2xl font-bold text-green-600 tracking-tight'>
|
||||
LibreTV
|
||||
MoonTV
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
@@ -39,6 +39,7 @@ interface SidebarProps {
|
||||
const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const [isCollapsed, setIsCollapsed] = useState(() => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
const saved = localStorage.getItem('sidebarCollapsed');
|
||||
@@ -47,8 +48,19 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
|
||||
const [active, setActive] = useState(activePath);
|
||||
|
||||
useEffect(() => {
|
||||
setActive(pathname);
|
||||
}, [pathname]);
|
||||
// 优先使用传入的 activePath
|
||||
if (activePath) {
|
||||
setActive(activePath);
|
||||
} else {
|
||||
// 否则使用当前路径
|
||||
const getCurrentFullPath = () => {
|
||||
const queryString = searchParams.toString();
|
||||
return queryString ? `${pathname}?${queryString}` : pathname;
|
||||
};
|
||||
const fullPath = getCurrentFullPath();
|
||||
setActive(fullPath);
|
||||
}
|
||||
}, [activePath, pathname, searchParams]);
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
const newState = !isCollapsed;
|
||||
@@ -66,12 +78,25 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{ icon: Tv, label: '电视剧', href: '/tv-shows' },
|
||||
{ icon: Film, label: '电影', href: '/movies' },
|
||||
{ icon: Star, label: '豆瓣 Top250', href: '/top250' },
|
||||
{ icon: Folder, label: '合集', href: '/collections' },
|
||||
{ icon: Star, label: '热门电影', href: '/douban/hot-movies' },
|
||||
{ icon: Star, label: '热门电视剧', href: '/douban/hot-tv' },
|
||||
{
|
||||
icon: Film,
|
||||
label: '热门电影',
|
||||
href: '/douban?type=movie&tag=热门&title=热门电影',
|
||||
},
|
||||
{
|
||||
icon: Tv,
|
||||
label: '热门剧集',
|
||||
href: '/douban?type=tv&tag=热门&title=热门剧集',
|
||||
},
|
||||
{
|
||||
icon: Star,
|
||||
label: '豆瓣 Top250',
|
||||
href: '/douban?type=movie&tag=top250&title=豆瓣 Top250',
|
||||
},
|
||||
{ icon: Folder, label: '美剧', href: '/douban?type=tv&tag=美剧' },
|
||||
{ icon: Folder, label: '韩剧', href: '/douban?type=tv&tag=韩剧' },
|
||||
{ icon: Folder, label: '日剧', href: '/douban?type=tv&tag=日剧' },
|
||||
{ icon: Folder, label: '日漫', href: '/douban?type=tv&tag=日本动画' },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -158,28 +183,44 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
|
||||
{/* 菜单项 */}
|
||||
<div className='flex-1 overflow-y-auto px-2 pt-4'>
|
||||
<div className='space-y-1'>
|
||||
{menuItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={() => setActive(item.href)}
|
||||
data-active={active === item.href}
|
||||
className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 transition-colors duration-200 min-h-[40px] ${
|
||||
isCollapsed
|
||||
? 'w-full max-w-none mx-0'
|
||||
: 'max-w-[220px] mx-auto'
|
||||
} gap-3 justify-start`}
|
||||
>
|
||||
<div className='w-4 h-4 flex items-center justify-center'>
|
||||
<item.icon className='h-4 w-4 text-gray-500 group-hover:text-green-600 group-data-[active=true]:text-green-700' />
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<span className='whitespace-nowrap transition-opacity duration-200 opacity-100'>
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
{menuItems.map((item) => {
|
||||
// 检查当前路径是否匹配这个菜单项
|
||||
const typeMatch = item.href.match(/type=([^&]+)/)?.[1];
|
||||
const tagMatch = item.href.match(/tag=([^&]+)/)?.[1];
|
||||
|
||||
// 解码URL以进行正确的比较
|
||||
const decodedActive = decodeURIComponent(active);
|
||||
const decodedItemHref = decodeURIComponent(item.href);
|
||||
|
||||
const isActive =
|
||||
decodedActive === decodedItemHref ||
|
||||
(decodedActive.startsWith('/douban') &&
|
||||
decodedActive.includes(`type=${typeMatch}`) &&
|
||||
decodedActive.includes(`tag=${tagMatch}`));
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={() => setActive(item.href)}
|
||||
data-active={isActive}
|
||||
className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 transition-colors duration-200 min-h-[40px] ${
|
||||
isCollapsed
|
||||
? 'w-full max-w-none mx-0'
|
||||
: 'max-w-[220px] mx-auto'
|
||||
} gap-3 justify-start`}
|
||||
>
|
||||
<div className='w-4 h-4 flex items-center justify-center'>
|
||||
<item.icon className='h-4 w-4 text-gray-500 group-hover:text-green-600 group-data-[active=true]:text-green-700' />
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<span className='whitespace-nowrap transition-opacity duration-200 opacity-100'>
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user