From 8ecb688127ed2ecb0e2b275b2d1a6c3fdf395043 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Thu, 26 Mar 2026 14:25:40 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=B5=E8=A7=86=E7=9B=B4=E6=92=AD=EF=BC=8C?= =?UTF-8?q?=E8=81=9A=E5=90=88=E5=90=8C=E5=90=8D=E8=8A=82=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/live/page.tsx | 259 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 222 insertions(+), 37 deletions(-) diff --git a/src/app/live/page.tsx b/src/app/live/page.tsx index 82e64f6..2020793 100644 --- a/src/app/live/page.tsx +++ b/src/app/live/page.tsx @@ -2,9 +2,9 @@ 'use client'; -import { Heart, Radio, Tv } from 'lucide-react'; +import { GitBranch, Heart, Radio, Tv } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { deleteFavorite, @@ -43,6 +43,21 @@ interface LiveChannel { url: string; } +type MergedChannelItem = + | { + type: 'single'; + key: string; + channel: LiveChannel; + } + | { + type: 'merged'; + key: string; + name: string; + group: string; + logo: string; + channels: LiveChannel[]; + }; + // 直播源接口 interface LiveSource { key: string; @@ -120,6 +135,7 @@ function LivePageClient() { // 搜索关键词 const [searchKeyword, setSearchKeyword] = useState(''); + const [expandedMergedChannels, setExpandedMergedChannels] = useState([]); // 节目单信息 const [epgData, setEpgData] = useState<{ @@ -1127,6 +1143,69 @@ function LivePageClient() { return filtered; }; + const mergedChannelItems = useMemo(() => { + if (!filteredChannels || filteredChannels.length === 0) return []; + + const mergedMap = new Map(); + const order: string[] = []; + + filteredChannels.forEach((channel) => { + const mergedKey = `${channel.group}::${channel.name.trim().toLowerCase()}`; + const existing = mergedMap.get(mergedKey); + + if (existing) { + existing.channels.push(channel); + if (!existing.logo && channel.logo) { + existing.logo = channel.logo; + } + return; + } + + mergedMap.set(mergedKey, { + key: mergedKey, + name: channel.name, + group: channel.group, + logo: channel.logo, + channels: [channel], + }); + order.push(mergedKey); + }); + + return order.map((key) => { + const item = mergedMap.get(key)!; + if (item.channels.length === 1) { + return { + type: 'single', + key, + channel: item.channels[0], + }; + } + + return { + type: 'merged', + key, + name: item.name, + group: item.group, + logo: item.logo, + channels: item.channels, + }; + }); + }, [filteredChannels]); + + const toggleMergedChannel = (key: string) => { + setExpandedMergedChannels((prev) => ( + prev.includes(key) + ? prev.filter(item => item !== key) + : [...prev, key] + )); + }; + // 切换分组 const handleGroupChange = (group: string) => { // 如果正在切换直播源,则禁用分组切换 @@ -2212,7 +2291,7 @@ function LivePageClient() { {/* 频道列表 */} -
@@ -2365,45 +2444,151 @@ function LivePageClient() { {/* 频道列表 */}
- {filteredChannels?.length > 0 ? ( - filteredChannels.map(channel => { - const isActive = channel.id === currentChannel?.id; + {mergedChannelItems?.length > 0 ? ( + mergedChannelItems.map(item => { + if (item.type === 'single') { + const channel = item.channel; + const isActive = channel.id === currentChannel?.id; + return ( + + ); + } + + const isExpanded = expandedMergedChannels.includes(item.key); + const activeLineIndex = item.channels.findIndex(channel => channel.id === currentChannel?.id); + const hasActiveChild = activeLineIndex !== -1; + return ( - + + + {isExpanded && ( +
+ {item.channels.map((channel, index) => { + const isActive = channel.id === currentChannel?.id; + return ( + + ); + })} +
+ )} +
); }) ) : (