876 lines
24 KiB
Vue
876 lines
24 KiB
Vue
<script setup lang="ts">
|
||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||
import { useI18n } from 'vue-i18n';
|
||
import StreamerFlipCard from './StreamerFlipCard.vue';
|
||
|
||
const { t } = useI18n();
|
||
|
||
// Mock data for live streams
|
||
const featuredStreams = computed(() => [
|
||
{
|
||
id: 1,
|
||
image: "https://imgproxy.goplayone.com/1/auto/768/0/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvYmFubmVyLzNlYTNkMzI0ZjJhMmZhNDQ5MDAyZWQyNDE0ZDNhZDhlLnBuZw==",
|
||
externalUrl: "https://twitch.tv/gamingking"
|
||
},
|
||
{
|
||
id: 2,
|
||
image: "https://imgproxy.goplayone.com/1/auto/768/0/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvYmFubmVyLzE5OGI4OGZhZmQ3ZDM2NjEyZmE0YTBmZDQ0NzViMmVjLnBuZw==",
|
||
externalUrl: "https://youtube.com/watch?v=music123"
|
||
},
|
||
{
|
||
id: 3,
|
||
image: "https://imgproxy.goplayone.com/1/auto/768/0/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvYmFubmVyLzVjMmM1NzRhNTY0ZDNhMmU4ODM5OTlhZTQ3NDk1NTQ5LnBuZw==",
|
||
externalUrl: "https://twitch.tv/cookingchef"
|
||
},
|
||
{
|
||
id: 4,
|
||
image: "https://imgproxy.goplayone.com/1/auto/768/0/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvYmFubmVyL2JjNGI4NmJlNzM5OWJlNGJmNTY2MTk0YjZmOWZkZDYwLnBuZw==",
|
||
externalUrl: "https://youtube.com/watch?v=coding123"
|
||
}
|
||
]);
|
||
|
||
const popularStreamers = computed(() => [
|
||
{
|
||
id: 1,
|
||
name: "音樂小天使",
|
||
photo: "https://playone-assets.goplayone.com/playone/user/play/avatar/ea249b64-a3d5-4ff1-bdc5-2eb0d0f956ea",
|
||
description: "音樂達人♫",
|
||
rank: "黃金",
|
||
fans: 1560,
|
||
orders: 112,
|
||
badges: ["rank-gold", "pro-music", "feature-verified"],
|
||
gender: "female" as const,
|
||
birthday: "1998-03-15",
|
||
greeting: "你好~我是音樂小天使~音樂達人♫",
|
||
status: "專業音樂陪陪!",
|
||
availability: "隨時可約 聲音甜美",
|
||
personality: "溫柔體貼 歌聲動人",
|
||
promise: "用音樂治癒你的心靈~快來聽我唱歌吧💕"
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "電競女武神",
|
||
photo: "https://imgproxy.goplayone.com/1/auto/244/244/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvdXNlci9wbGF5L2F2YXRhci83NDk5ODA2OS04YTU4LTQ5YWQtOTc1YS1jNTczZmY0YjNjNTY=",
|
||
description: "FPS女王",
|
||
rank: "大師",
|
||
fans: 2800,
|
||
orders: 198,
|
||
badges: ["rank-master", "pro-gaming", "feature-live"],
|
||
gender: "female" as const,
|
||
birthday: "1995-07-22",
|
||
greeting: "Yo~我是電競女武神~FPS女王🔫",
|
||
status: "前職業選手 現役陪陪!",
|
||
availability: "晚上8-12點 週末全天",
|
||
personality: "冷靜狙擊 一槍一個",
|
||
promise: "帶你體驗職業級操作!從菜鳥到高手 包教包會🎯"
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "二次元萌妹",
|
||
photo: "https://playone-assets.goplayone.com/playone/user/play/avatar/a4b858a7-a1f3-4543-8d18-ae94e8db573b",
|
||
description: "動漫專家",
|
||
rank: "黃金",
|
||
fans: 1200,
|
||
orders: 89,
|
||
badges: ["rank-gold", "special-anime", "feature-new"],
|
||
gender: "female" as const,
|
||
birthday: "2000-11-08",
|
||
greeting: "こんにちは~我是二次元萌妹~動漫專家🌸",
|
||
status: "動漫系大學生 兼職陪陪!",
|
||
availability: "平日晚上 週末下午",
|
||
personality: "超愛動漫 聲音超萌",
|
||
promise: "一起討論最新番劇!陪你刷副本 收集老婆💖"
|
||
},
|
||
{
|
||
id: 4,
|
||
name: "策略大師",
|
||
photo: "https://imgproxy.goplayone.com/1/auto/244/244/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvdXNlci9wbGF5L2F2YXRhci8zZjMzMTZlYS04YzRkLTQ0Y2UtOTc5Mi0zMTk2ZmUxZmJhOGU=",
|
||
description: "戰術專家",
|
||
rank: "鑽石",
|
||
fans: 4200,
|
||
orders: 312,
|
||
badges: ["rank-diamond", "achievement-expert", "feature-vip"],
|
||
gender: "male" as const,
|
||
birthday: "1992-05-18",
|
||
greeting: "你好~我是策略大師~戰術專家🧠",
|
||
status: "前職業教練 現專職陪陪!",
|
||
availability: "週一到週五 下午2-8點",
|
||
personality: "理性分析 耐心指導",
|
||
promise: "從戰術思維到操作細節 全面提升你的遊戲智商!📊"
|
||
},
|
||
{
|
||
id: 5,
|
||
name: "派對女王",
|
||
photo: "https://imgproxy.goplayone.com/1/auto/244/244/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvdXNlci9wbGF5L2F2YXRhci80YmUzOWUxZi04YzJhLTRiMmItYjk0NS1lYjAyMWEzNjc5ZGM=",
|
||
description: "社交達人",
|
||
rank: "鑽石",
|
||
fans: 2100,
|
||
orders: 167,
|
||
badges: ["rank-diamond", "special-party", "feature-popular"],
|
||
gender: "female" as const,
|
||
birthday: "1996-09-12",
|
||
greeting: "Hey~我是派對女王~社交達人🎉",
|
||
status: "全職陪陪 專攻社交遊戲!",
|
||
availability: "24小時待命 隨時開趴",
|
||
personality: "超會帶氣氛 人緣超好",
|
||
promise: "讓你的遊戲時光充滿歡笑!組隊開黑 一起嗨翻天🎊"
|
||
},
|
||
{
|
||
id: 6,
|
||
name: "生存專家",
|
||
photo: "https://imgproxy.goplayone.com/1/auto/420/420/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvdXNlci9wbGF5L2F2YXRhci9hYTFhNzdjMy05YTI2LTRmNTctOTlkMC02NTA2M2IwMDgyYTY=",
|
||
description: "荒野求生",
|
||
rank: "大師",
|
||
fans: 1800,
|
||
orders: 134,
|
||
badges: ["rank-master", "special-survival", "achievement-mentor"],
|
||
gender: "male" as const,
|
||
birthday: "1994-12-03",
|
||
greeting: "Hello~我是生存專家~荒野求生🏕️",
|
||
status: "建築系學生 兼職陪陪!",
|
||
availability: "晚上7點後 週末全天",
|
||
personality: "創意無限 耐心建造",
|
||
promise: "帶你建造夢想家園!從零開始 打造專屬世界🏗️"
|
||
}
|
||
]);
|
||
|
||
// Mock data for recommended services
|
||
const recommendedServices = computed(() => [
|
||
{
|
||
id: 1,
|
||
name: "1v1 聊天",
|
||
nameEn: "1v1 Chat",
|
||
image: "https://imgproxy.goplayone.com/1/auto/244/94/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvc2tpbGwvYzQ4YzUxYjE1YmUyZTFjMDcyOTk1ZGJhZGE0MmExY2EucG5n",
|
||
description: "一對一聊天服務",
|
||
category: "chat"
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "唱歌",
|
||
nameEn: "Singing",
|
||
image: "http://imgproxy.goplayone.com/1/auto/244/94/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvc2tpbGwvYzY1ZTJhMjAzYzRlY2U2NzhkNjkxNGE3YTBhMmQ0ODMucG5n",
|
||
description: "音樂歌唱服務",
|
||
category: "music"
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "STEAM",
|
||
nameEn: "STEAM",
|
||
image: "https://imgproxy.goplayone.com/1/auto/244/94/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvc2tpbGwvM2RmOGQ2NzdiYzZmMGIzMzIzZmQ1MGNhYWFhZDUyMjUucG5n",
|
||
description: "Steam 遊戲平台",
|
||
category: "gaming"
|
||
},
|
||
{
|
||
id: 4,
|
||
name: "英雄聯盟",
|
||
nameEn: "League of Legends",
|
||
image: "https://imgproxy.goplayone.com/1/auto/244/94/sm/0/aHR0cHM6Ly9wbGF5b25lLWFzc2V0cy5nb3BsYXlvbmUuY29tL3BsYXlvbmUvc2tpbGwvMTljZmUxYWYwMGYyN2M4YWY1ZTYzOGFkNjM0ZDNkMDYucG5n",
|
||
description: "英雄聯盟遊戲",
|
||
category: "gaming"
|
||
},
|
||
{
|
||
id: 5,
|
||
name: "王者榮耀",
|
||
nameEn: "Honor of Kings",
|
||
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDIwMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImtpbmdHcmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+CjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNGRkQ3MDAiLz4KPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjRkZBNTAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMjAiIGZpbGw9InVybCgja2luZ0dyYWRpZW50KSIvPgo8dGV4dCB4PSIxMDAiIHk9IjYwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSJib2xkIiBmaWxsPSJ3aGl0ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+546L5a2Q5rW35rKzPC90ZXh0Pgo8c3ZnIHg9IjgwIiB5PSIzMCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjRkZGRkZGIj4KPGNpcmNsZSBjeD0iMjAiIGN5PSIyMCIgcj0iMTgiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxwYXRoIGQ9Ik0xMCAxMCBMMzAgMzAgTTMwIDEwIEwxMCAzMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cjwvc3ZnPgo=",
|
||
description: "王者榮耀手遊",
|
||
category: "gaming"
|
||
},
|
||
{
|
||
id: 6,
|
||
name: "原神",
|
||
nameEn: "Genshin Impact",
|
||
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDIwMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImdlbnNoaW5HcmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+CjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2NkMzRkYiLz4KPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDA2NkZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMjAiIGZpbGw9InVybCgjZ2Vuc2hpbkdyYWRpZW50KSIvPgo8dGV4dCB4PSIxMDAiIHk9IjYwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMjQiIGZvbnQtd2VpZ2h0PSJib2xkIiBmaWxsPSJ3aGl0ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+5Y6f5YibPC90ZXh0Pgo8c3ZnIHg9IjgwIiB5PSIzMCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjRkZGRkZGIj4KPGNpcmNsZSBjeD0iMjAiIGN5PSIyMCIgcj0iMTgiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxwYXRoIGQ9Ik0xMCAxMCBMMzAgMzAgTTMwIDEwIEwxMCAzMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cjwvc3ZnPgo=",
|
||
description: "原神開放世界遊戲",
|
||
category: "gaming"
|
||
},
|
||
{
|
||
id: 7,
|
||
name: "直播",
|
||
nameEn: "Live Streaming",
|
||
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDIwMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImxpdmVHcmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+CjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNGRjAwMDAiLz4KPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjQ0MwMDAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMjAiIGZpbGw9InVybCgjbGl2ZUdyYWRpZW50KSIvPgo8dGV4dCB4PSIxMDAiIHk9IjYwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMjQiIGZvbnQtd2VpZ2h0PSJib2xkIiBmaWxsPSJ3aGl0ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+55m75b2VPC90ZXh0Pgo8c3ZnIHg9IjgwIiB5PSIzMCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjRkZGRkZGIj4KPGNpcmNsZSBjeD0iMjAiIGN5PSIyMCIgcj0iMTgiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxwYXRoIGQ9Ik0xMCAxMCBMMzAgMzAgTTMwIDEwIEwxMCAzMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cjwvc3ZnPgo=",
|
||
description: "直播服務",
|
||
category: "streaming"
|
||
},
|
||
{
|
||
id: 8,
|
||
name: "陪玩",
|
||
nameEn: "Gaming Companion",
|
||
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDIwMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImNvbXBhbm9uR3JhZGllbnQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPgo8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjRkY2NkNDIi8+CjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0ZGMDA5OSIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTIwIiBmaWxsPSJ1cmwoI2NvbXBhbm9uR3JhZGllbnQpIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNjAiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIyNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGZpbGw9IndoaXRlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7mh6Dmh6A8L3RleHQ+CjxzdmcgeD0iODAiIHk9IjMwIiB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIGZpbGw9IiNGRkZGRkYiPgo8Y2lyY2xlIGN4PSIyMCIgY3k9IjIwIiByPSIxOCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTEwIDEwIEwzMCAzMCBNMzAgMTAgTDEwIDMwIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIvPgo8L3N2Zz4KPC9zdmc+Cg==",
|
||
description: "遊戲陪玩服務",
|
||
category: "companion"
|
||
}
|
||
]);
|
||
|
||
// Services drag state
|
||
const isDragging = ref(false);
|
||
const startX = ref(0);
|
||
const scrollLeft = ref(0);
|
||
const servicesContainer = ref<HTMLElement | null>(null);
|
||
|
||
// Carousel state
|
||
const currentSlide = ref(0);
|
||
const carouselInterval = ref<number | null>(null);
|
||
|
||
|
||
// Auto-play carousel
|
||
const startCarousel = () => {
|
||
carouselInterval.value = window.setInterval(() => {
|
||
currentSlide.value = (currentSlide.value + 1) % featuredStreams.value.length;
|
||
}, 4000);
|
||
};
|
||
|
||
const stopCarousel = () => {
|
||
if (carouselInterval.value) {
|
||
clearInterval(carouselInterval.value);
|
||
carouselInterval.value = null;
|
||
}
|
||
};
|
||
|
||
|
||
// Handle stream click
|
||
const handleStreamClick = (stream: any) => {
|
||
if (stream.externalUrl) {
|
||
window.open(stream.externalUrl, '_blank', 'noopener,noreferrer');
|
||
}
|
||
};
|
||
|
||
// Handle streamer click
|
||
const handleStreamerClick = (streamer: any) => {
|
||
console.log(`查看主播: ${streamer.name}`);
|
||
// TODO: Open streamer profile
|
||
};
|
||
|
||
// Handle more button click
|
||
const handleMoreClick = (streamer: any) => {
|
||
console.log(`查看更多: ${streamer.name}`);
|
||
// TODO: Open detailed streamer page
|
||
};
|
||
|
||
// Handle profile button click
|
||
const handleProfileClick = (streamer: any) => {
|
||
console.log(`前往主播主頁: ${streamer.name}`);
|
||
// TODO: Navigate to streamer profile page
|
||
};
|
||
|
||
// Handle service click
|
||
const handleServiceClick = (service: any) => {
|
||
console.log(`點擊服務: ${service.name}`);
|
||
// TODO: Navigate to service page or open service modal
|
||
};
|
||
|
||
// Handle service drag functionality
|
||
const handleMouseDown = (e: MouseEvent) => {
|
||
if (!servicesContainer.value) return;
|
||
|
||
isDragging.value = true;
|
||
startX.value = e.pageX - servicesContainer.value.offsetLeft;
|
||
scrollLeft.value = servicesContainer.value.scrollLeft;
|
||
|
||
// Prevent text selection while dragging
|
||
e.preventDefault();
|
||
};
|
||
|
||
const handleMouseMove = (e: MouseEvent) => {
|
||
if (!isDragging.value || !servicesContainer.value) return;
|
||
|
||
e.preventDefault();
|
||
const x = e.pageX - servicesContainer.value.offsetLeft;
|
||
const walk = (x - startX.value) * 2; // Multiply for faster scrolling
|
||
servicesContainer.value.scrollLeft = scrollLeft.value - walk;
|
||
};
|
||
|
||
const handleMouseUp = () => {
|
||
isDragging.value = false;
|
||
};
|
||
|
||
const handleMouseLeave = () => {
|
||
isDragging.value = false;
|
||
};
|
||
|
||
// Handle more services button click
|
||
const handleMoreServicesClick = () => {
|
||
console.log('點擊更多服務');
|
||
// TODO: Navigate to services page
|
||
};
|
||
|
||
|
||
onMounted(() => {
|
||
startCarousel();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
stopCarousel();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="news-hub">
|
||
<!-- Header -->
|
||
<div class="hub-header">
|
||
<h2 class="hub-title">📰 {{ t('livestream.title') }}</h2>
|
||
<div class="hub-subtitle">{{ t('livestream.subtitle') }}</div>
|
||
</div>
|
||
|
||
<!-- Home View -->
|
||
<div class="home-view">
|
||
|
||
<!-- Featured Streams Carousel -->
|
||
<div class="carousel-section">
|
||
<h3 class="section-title">🔥 {{ t('livestream.featuredStreams') }}</h3>
|
||
<div class="carousel-container" @mouseenter="stopCarousel" @mouseleave="startCarousel">
|
||
<div class="carousel-track" :style="{ transform: `translateX(-${currentSlide * 100}%)` }">
|
||
<div
|
||
v-for="stream in featuredStreams"
|
||
:key="stream.id"
|
||
class="carousel-slide"
|
||
@click="handleStreamClick(stream)"
|
||
>
|
||
<div class="stream-image-card">
|
||
<img :src="stream.image" :alt="`Stream ${stream.id}`" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Carousel Controls -->
|
||
<button
|
||
class="carousel-btn prev"
|
||
@click="currentSlide = currentSlide > 0 ? currentSlide - 1 : featuredStreams.length - 1"
|
||
>
|
||
‹
|
||
</button>
|
||
<button
|
||
class="carousel-btn next"
|
||
@click="currentSlide = (currentSlide + 1) % featuredStreams.length"
|
||
>
|
||
›
|
||
</button>
|
||
|
||
<!-- Carousel Indicators -->
|
||
<div class="carousel-indicators">
|
||
<button
|
||
v-for="(stream, index) in featuredStreams"
|
||
:key="index"
|
||
class="indicator"
|
||
:class="{ active: index === currentSlide }"
|
||
@click="currentSlide = index"
|
||
></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recommended Services -->
|
||
<div class="services-section">
|
||
<div class="services-header">
|
||
<h3 class="section-title">🎯 {{ t('livestream.recommendedServices') }}</h3>
|
||
<button class="more-services-btn" @click="handleMoreServicesClick">
|
||
更多
|
||
</button>
|
||
</div>
|
||
|
||
<div
|
||
ref="servicesContainer"
|
||
class="services-scroll-container"
|
||
@mousedown="handleMouseDown"
|
||
@mousemove="handleMouseMove"
|
||
@mouseup="handleMouseUp"
|
||
@mouseleave="handleMouseLeave"
|
||
:class="{ 'dragging': isDragging }"
|
||
>
|
||
<div class="services-grid">
|
||
<div
|
||
v-for="service in recommendedServices"
|
||
:key="service.id"
|
||
class="service-card"
|
||
@click="handleServiceClick(service)"
|
||
>
|
||
<div class="service-image">
|
||
<img :src="service.image" :alt="service.name" />
|
||
</div>
|
||
<div class="service-label">{{ service.name }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Popular Streamers -->
|
||
<div class="streamers-section">
|
||
<h3 class="section-title">⭐ {{ t('livestream.popularStreamers') }}</h3>
|
||
<div class="streamers-grid">
|
||
<StreamerFlipCard
|
||
v-for="streamer in popularStreamers"
|
||
:key="streamer.id"
|
||
:streamer="streamer"
|
||
@click="handleStreamerClick"
|
||
@more="handleMoreClick"
|
||
@profile="handleProfileClick"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div> <!-- End home-view -->
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.news-hub {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--window-background);
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.hub-header {
|
||
text-align: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.hub-title {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: var(--content-text-color);
|
||
margin: 0 0 8px 0;
|
||
}
|
||
|
||
.hub-subtitle {
|
||
font-size: 14px;
|
||
color: var(--content-text-color);
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.carousel-section {
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: var(--content-text-color);
|
||
margin: 0 0 16px 0;
|
||
}
|
||
|
||
.carousel-container {
|
||
position: relative;
|
||
width: 100%;
|
||
aspect-ratio: 16/9;
|
||
overflow: hidden;
|
||
border-radius: 12px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.carousel-track {
|
||
display: flex;
|
||
width: 100%;
|
||
height: 100%;
|
||
transition: transform 0.5s ease-in-out;
|
||
}
|
||
|
||
.carousel-slide {
|
||
min-width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.stream-image-card {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
cursor: pointer;
|
||
transition: transform 0.3s ease;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stream-image-card img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center;
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
.stream-image-card:hover img {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.7; }
|
||
}
|
||
|
||
.carousel-btn {
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: none;
|
||
color: white;
|
||
font-size: 24px;
|
||
width: 50px;
|
||
height: 50px;
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
z-index: 10;
|
||
backdrop-filter: blur(10px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.carousel-btn:hover {
|
||
background: rgba(0, 0, 0, 0.7);
|
||
transform: translateY(-50%) scale(1.1);
|
||
}
|
||
|
||
.carousel-btn.prev {
|
||
left: 16px;
|
||
}
|
||
|
||
.carousel-btn.next {
|
||
right: 16px;
|
||
}
|
||
|
||
.carousel-indicators {
|
||
position: absolute;
|
||
bottom: 16px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
gap: 8px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.indicator {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||
background: rgba(255, 255, 255, 0.3);
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
flex-shrink: 0;
|
||
aspect-ratio: 1;
|
||
min-width: 12px;
|
||
min-height: 12px;
|
||
}
|
||
|
||
.indicator.active {
|
||
background: white;
|
||
border-color: white;
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
.services-section {
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.services-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.more-services-btn {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.more-services-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.services-scroll-container {
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
cursor: grab;
|
||
border-radius: 12px;
|
||
scrollbar-width: none; /* Firefox */
|
||
-ms-overflow-style: none; /* IE and Edge */
|
||
}
|
||
|
||
.services-scroll-container::-webkit-scrollbar {
|
||
display: none; /* Chrome, Safari, Opera */
|
||
}
|
||
|
||
.services-scroll-container.dragging {
|
||
cursor: grabbing;
|
||
user-select: none;
|
||
}
|
||
|
||
.services-grid {
|
||
display: flex;
|
||
gap: 24px;
|
||
padding: 0 12px;
|
||
min-width: max-content;
|
||
}
|
||
|
||
.service-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||
width: 200px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.service-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.service-image {
|
||
width: 100%;
|
||
height: 120px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.service-image img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center;
|
||
}
|
||
|
||
.service-label {
|
||
padding: 12px 16px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
background: white;
|
||
}
|
||
|
||
|
||
.streamers-section {
|
||
flex: 1;
|
||
}
|
||
|
||
.streamers-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 24px;
|
||
align-items: start;
|
||
}
|
||
|
||
|
||
/* 響應式網格佈局 */
|
||
/* 響應式設計 */
|
||
@media (max-width: 1200px) {
|
||
.services-grid {
|
||
gap: 20px;
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.service-card {
|
||
width: 250px;
|
||
}
|
||
|
||
.streamers-grid {
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.carousel-container {
|
||
aspect-ratio: 16/9;
|
||
}
|
||
|
||
.carousel-btn {
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 20px;
|
||
}
|
||
|
||
.carousel-btn.prev {
|
||
left: 12px;
|
||
}
|
||
|
||
.carousel-btn.next {
|
||
right: 12px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.news-hub {
|
||
padding: 16px;
|
||
}
|
||
|
||
.hub-title {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.hub-subtitle {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.services-grid {
|
||
gap: 16px;
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.service-card {
|
||
width: 180px;
|
||
}
|
||
|
||
.service-image {
|
||
height: 100px;
|
||
}
|
||
|
||
.service-label {
|
||
padding: 10px 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.more-services-btn {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.streamers-grid {
|
||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.carousel-container {
|
||
aspect-ratio: 16/9;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.carousel-btn {
|
||
width: 36px;
|
||
height: 36px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.carousel-btn.prev {
|
||
left: 8px;
|
||
}
|
||
|
||
.carousel-btn.next {
|
||
right: 8px;
|
||
}
|
||
|
||
.carousel-indicators {
|
||
bottom: 12px;
|
||
gap: 6px;
|
||
}
|
||
|
||
.indicator {
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.news-hub {
|
||
padding: 12px;
|
||
}
|
||
|
||
.hub-title {
|
||
font-size: 20px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.hub-subtitle {
|
||
font-size: 13px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.services-grid {
|
||
gap: 12px;
|
||
padding: 0 8px;
|
||
}
|
||
|
||
.service-card {
|
||
width: 150px;
|
||
}
|
||
|
||
.service-image {
|
||
height: 80px;
|
||
}
|
||
|
||
.service-label {
|
||
padding: 8px 10px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.more-services-btn {
|
||
padding: 4px 8px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.streamers-grid {
|
||
grid-template-columns: 1fr;
|
||
gap: 16px;
|
||
}
|
||
|
||
.carousel-container {
|
||
aspect-ratio: 16/9;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.carousel-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.carousel-btn.prev {
|
||
left: 6px;
|
||
}
|
||
|
||
.carousel-btn.next {
|
||
right: 6px;
|
||
}
|
||
|
||
.carousel-indicators {
|
||
bottom: 8px;
|
||
gap: 4px;
|
||
}
|
||
|
||
.indicator {
|
||
width: 8px;
|
||
height: 8px;
|
||
}
|
||
}
|
||
|
||
/* Light theme adjustments */
|
||
.theme-light .carousel-btn {
|
||
background: rgba(255, 255, 255, 0.8);
|
||
color: #333;
|
||
}
|
||
|
||
.theme-light .carousel-btn:hover {
|
||
background: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.theme-light .indicator {
|
||
border-color: rgba(0, 0, 0, 0.6);
|
||
background: rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.theme-light .indicator.active {
|
||
background: #333;
|
||
border-color: #333;
|
||
}
|
||
|
||
</style>
|