feat: add news
This commit is contained in:
parent
5e7534966d
commit
61b559b20b
|
@ -7,7 +7,7 @@ import { useDraggable } from '../composables/useDraggable';
|
|||
import type { SnapType } from '../composables/useDraggable';
|
||||
import { useResizable } from '../composables/useResizable';
|
||||
import { useBreakpoint } from '../composables/useBreakpoint';
|
||||
import LiveStreamHub from './LiveStreamHub.vue';
|
||||
import NewsHub from './NewsHub.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
instance: AppInstance;
|
||||
|
@ -125,7 +125,7 @@ function onMouseDown() {
|
|||
const appComponent = computed(() => {
|
||||
switch (props.instance.appId) {
|
||||
case 'livestream-hub':
|
||||
return LiveStreamHub;
|
||||
return NewsHub;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,401 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
// Mock data for live streams
|
||||
const featuredStreams = computed(() => [
|
||||
{
|
||||
id: 1,
|
||||
image: "https://picsum.photos/800/400?random=1",
|
||||
externalUrl: "https://twitch.tv/gamingking"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: "https://picsum.photos/800/400?random=2",
|
||||
externalUrl: "https://youtube.com/watch?v=music123"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
image: "https://picsum.photos/800/400?random=3",
|
||||
externalUrl: "https://twitch.tv/cookingchef"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
image: "https://picsum.photos/800/400?random=4",
|
||||
externalUrl: "https://youtube.com/watch?v=coding123"
|
||||
}
|
||||
]);
|
||||
|
||||
const popularStreamers = computed(() => [
|
||||
{ id: 1, name: t('livestream.streamers.gamingKing'), avatar: "🎮", viewers: 15420, isLive: true },
|
||||
{ id: 2, name: t('livestream.streamers.musicMaster'), avatar: "🎵", viewers: 8930, isLive: true },
|
||||
{ id: 3, name: t('livestream.streamers.cookingChef'), avatar: "🍳", viewers: 5670, isLive: true },
|
||||
{ id: 4, name: t('livestream.streamers.codeTeacher'), avatar: "💻", viewers: 12300, isLive: true },
|
||||
{ id: 5, name: t('livestream.streamers.artist'), avatar: "🎨", viewers: 4200, isLive: true },
|
||||
{ id: 6, name: t('livestream.streamers.trainer'), avatar: "💪", viewers: 6800, isLive: false },
|
||||
{ id: 7, name: t('livestream.streamers.traveler'), avatar: "✈️", viewers: 3200, isLive: true },
|
||||
{ id: 8, name: t('livestream.streamers.petLover'), avatar: "🐱", viewers: 5100, isLive: false }
|
||||
]);
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
startCarousel();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopCarousel();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="livestream-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"
|
||||
:style="{ backgroundImage: `url(${stream.image})` }"
|
||||
>
|
||||
</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>
|
||||
|
||||
<!-- Popular Streamers -->
|
||||
<div class="streamers-section">
|
||||
<h3 class="section-title">⭐ {{ t('livestream.popularStreamers') }}</h3>
|
||||
<div class="streamers-grid">
|
||||
<div
|
||||
v-for="streamer in popularStreamers"
|
||||
:key="streamer.id"
|
||||
class="streamer-card"
|
||||
@click="handleStreamerClick(streamer)"
|
||||
>
|
||||
<div class="streamer-avatar">{{ streamer.avatar }}</div>
|
||||
<div class="streamer-info">
|
||||
<div class="streamer-name">{{ streamer.name }}</div>
|
||||
<div class="streamer-viewers">{{ streamer.viewers }} {{ t('livestream.viewers') }}</div>
|
||||
</div>
|
||||
<div class="streamer-status" :class="{ 'is-live': streamer.isLive }">
|
||||
{{ streamer.isLive ? `🔴 ${t('livestream.live')}` : `⚪ ${t('livestream.offline')}` }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End home-view -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.livestream-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%;
|
||||
height: 300px;
|
||||
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%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stream-image-card:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
.streamers-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.streamers-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.streamer-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
background: var(--window-background);
|
||||
border: 1px solid var(--window-border-color);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.streamer-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--window-border-color-focused);
|
||||
}
|
||||
|
||||
.streamer-avatar {
|
||||
font-size: 32px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--title-bar-background);
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.streamer-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.streamer-name {
|
||||
font-weight: bold;
|
||||
color: var(--content-text-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.streamer-viewers {
|
||||
font-size: 12px;
|
||||
color: var(--content-text-color);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.streamer-status {
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.streamer-status.is-live {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* 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>
|
|
@ -0,0 +1,875 @@
|
|||
<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>
|
|
@ -0,0 +1,342 @@
|
|||
<template>
|
||||
<div :class="['streamer-badge', badgeClass, sizeClass]" :title="badgeConfig.tooltip">
|
||||
<span v-if="badgeConfig.icon" class="badge-icon">{{ badgeConfig.icon }}</span>
|
||||
<span v-if="badgeConfig.text" class="badge-text">{{ badgeConfig.text }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
interface Props {
|
||||
code: string;
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const badgeConfigs: { [key: string]: { text: string; icon?: string; class: string; tooltip: string; } } = {
|
||||
// 等級徽章
|
||||
'rank-diamond': { text: '鑽石', icon: '💎', class: 'badge-rank-diamond', tooltip: '鑽石級玩家' },
|
||||
'rank-gold': { text: '黃金', icon: '🏆', class: 'badge-rank-gold', tooltip: '黃金級玩家' },
|
||||
'rank-silver': { text: '白銀', icon: '🥈', class: 'badge-rank-silver', tooltip: '白銀級玩家' },
|
||||
'rank-bronze': { text: '青銅', icon: '🥉', class: 'badge-rank-bronze', tooltip: '青銅級玩家' },
|
||||
'rank-master': { text: '大師', icon: '🎖️', class: 'badge-rank-master', tooltip: '大師級玩家' },
|
||||
|
||||
// 專業徽章
|
||||
'pro-gaming': { text: '電競', icon: '🎮', class: 'badge-pro-gaming', tooltip: '專業電競陪玩' },
|
||||
'pro-music': { text: '音樂', icon: '🎵', class: 'badge-pro-music', tooltip: '音樂專家' },
|
||||
'pro-anime': { text: '二次元', icon: '🌸', class: 'badge-pro-anime', tooltip: '二次元專家' },
|
||||
'pro-party': { text: '派對', icon: '🎉', class: 'badge-pro-party', tooltip: '派對達人' },
|
||||
'pro-strategy': { text: '策略', icon: '♟️', class: 'badge-pro-strategy', tooltip: '策略大師' },
|
||||
'pro-survival': { text: '生存', icon: '🏹', class: 'badge-pro-survival', tooltip: '生存專家' },
|
||||
|
||||
// 特色徽章
|
||||
'feature-live': { text: '直播中', icon: '🔴', class: 'badge-feature-live', tooltip: '正在直播' },
|
||||
'feature-verified': { text: '認證', icon: '✅', class: 'badge-feature-verified', tooltip: '認證主播' },
|
||||
'feature-new': { text: '新人', icon: '⭐', class: 'badge-feature-new', tooltip: '新進主播' },
|
||||
'feature-popular': { text: '熱門', icon: '🔥', class: 'badge-feature-popular', tooltip: '熱門主播' },
|
||||
'feature-vip': { text: 'VIP', icon: '👑', class: 'badge-feature-vip', tooltip: 'VIP主播' },
|
||||
|
||||
// 特殊徽章
|
||||
'special-anime': { text: '動漫', icon: '🎌', class: 'badge-special-anime', tooltip: '動漫專家' },
|
||||
'special-party': { text: '派對', icon: '🎊', class: 'badge-special-party', tooltip: '派對女王' },
|
||||
'special-survival': { text: '生存', icon: '🛡️', class: 'badge-special-survival', tooltip: '生存專家' },
|
||||
|
||||
// 成就徽章
|
||||
'achievement-expert': { text: '專家', icon: '🎯', class: 'badge-achievement-expert', tooltip: '領域專家' },
|
||||
'achievement-mentor': { text: '導師', icon: '👨🏫', class: 'badge-achievement-mentor', tooltip: '新手導師' }
|
||||
};
|
||||
|
||||
const badgeConfig = computed(() => badgeConfigs[props.code] || { text: props.code, class: 'badge-default', tooltip: props.code });
|
||||
const badgeClass = computed(() => badgeConfig.value.class);
|
||||
const sizeClass = computed(() => `badge-size-${props.size || 'medium'}`);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.streamer-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.streamer-badge:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.badge-icon {
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.badge-text {
|
||||
font-size: 9px;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 尺寸變體 */
|
||||
.badge-size-small {
|
||||
padding: 2px 6px;
|
||||
font-size: 8px;
|
||||
border-radius: 8px;
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.badge-size-small .badge-icon {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.badge-size-small .badge-text {
|
||||
font-size: 7px;
|
||||
}
|
||||
|
||||
.badge-size-medium {
|
||||
padding: 4px 8px;
|
||||
font-size: 10px;
|
||||
border-radius: 12px;
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.badge-size-large {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-radius: 16px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.badge-size-large .badge-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.badge-size-large .badge-text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* 等級徽章 */
|
||||
.badge-rank-diamond {
|
||||
background: linear-gradient(135deg, #b9f2ff 0%, #00d4ff 100%);
|
||||
color: #0066cc;
|
||||
border-color: rgba(0, 102, 204, 0.3);
|
||||
}
|
||||
|
||||
.badge-rank-gold {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb347 100%);
|
||||
color: #b8860b;
|
||||
border-color: rgba(184, 134, 11, 0.3);
|
||||
}
|
||||
|
||||
.badge-rank-silver {
|
||||
background: linear-gradient(135deg, #c0c0c0 0%, #a8a8a8 100%);
|
||||
color: #696969;
|
||||
border-color: rgba(105, 105, 105, 0.3);
|
||||
}
|
||||
|
||||
.badge-rank-bronze {
|
||||
background: linear-gradient(135deg, #cd7f32 0%, #b87333 100%);
|
||||
color: #8b4513;
|
||||
border-color: rgba(139, 69, 19, 0.3);
|
||||
}
|
||||
|
||||
.badge-rank-master {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ff8c00 100%);
|
||||
color: #8b4513;
|
||||
border-color: rgba(139, 69, 19, 0.3);
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 專業徽章 */
|
||||
.badge-pro-gaming {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.badge-pro-music {
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #7fcdcd 100%);
|
||||
color: #2c5530;
|
||||
border-color: rgba(44, 85, 48, 0.3);
|
||||
}
|
||||
|
||||
.badge-pro-anime {
|
||||
background: linear-gradient(135deg, #ffb3ba 0%, #ffdfba 100%);
|
||||
color: #d63384;
|
||||
border-color: rgba(214, 51, 132, 0.3);
|
||||
}
|
||||
|
||||
.badge-pro-party {
|
||||
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||||
color: #e91e63;
|
||||
border-color: rgba(233, 30, 99, 0.3);
|
||||
}
|
||||
|
||||
.badge-pro-strategy {
|
||||
background: linear-gradient(135deg, #a8c8ec 0%, #5d9cec 100%);
|
||||
color: #2c3e50;
|
||||
border-color: rgba(44, 62, 80, 0.3);
|
||||
}
|
||||
|
||||
.badge-pro-survival {
|
||||
background: linear-gradient(135deg, #d4a574 0%, #8b7355 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 特殊徽章 */
|
||||
.badge-special-anime {
|
||||
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||||
color: #e91e63;
|
||||
border-color: rgba(233, 30, 99, 0.3);
|
||||
}
|
||||
|
||||
.badge-special-party {
|
||||
background: linear-gradient(135deg, #ff6b9d 0%, #c44569 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.badge-special-survival {
|
||||
background: linear-gradient(135deg, #8b7355 0%, #6b5b73 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 特色徽章 */
|
||||
.badge-feature-live {
|
||||
background: linear-gradient(135deg, #ff4757 0%, #c44569 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.badge-feature-verified {
|
||||
background: linear-gradient(135deg, #2ed573 0%, #1e90ff 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.badge-feature-new {
|
||||
background: linear-gradient(135deg, #ffa502 0%, #ff6348 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.badge-feature-popular {
|
||||
background: linear-gradient(135deg, #ff3838 0%, #ff6b35 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
animation: glow 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.badge-feature-vip {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ff8c00 100%);
|
||||
color: #8b4513;
|
||||
border-color: rgba(139, 69, 19, 0.3);
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 成就徽章 */
|
||||
.badge-achievement-expert {
|
||||
background: linear-gradient(135deg, #2ed573 0%, #1e90ff 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
animation: glow 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.badge-achievement-mentor {
|
||||
background: linear-gradient(135deg, #ffa502 0%, #ff6348 100%);
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 預設徽章 */
|
||||
.badge-default {
|
||||
background: linear-gradient(135deg, #ddd 0%, #bbb 100%);
|
||||
color: #666;
|
||||
border-color: rgba(102, 102, 102, 0.3);
|
||||
}
|
||||
|
||||
/* 動畫效果 */
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from { box-shadow: 0 2px 8px rgba(255, 56, 56, 0.3); }
|
||||
to { box-shadow: 0 4px 16px rgba(255, 56, 56, 0.6); }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
.badge-feature-vip {
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 響應式設計 */
|
||||
@media (max-width: 768px) {
|
||||
.streamer-badge {
|
||||
padding: 3px 6px;
|
||||
font-size: 9px;
|
||||
border-radius: 10px;
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
.badge-size-small {
|
||||
padding: 2px 4px;
|
||||
font-size: 7px;
|
||||
border-radius: 6px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.badge-size-large {
|
||||
padding: 4px 8px;
|
||||
font-size: 10px;
|
||||
border-radius: 12px;
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.streamer-badge {
|
||||
padding: 2px 5px;
|
||||
font-size: 8px;
|
||||
border-radius: 8px;
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.badge-size-small {
|
||||
padding: 1px 3px;
|
||||
font-size: 6px;
|
||||
border-radius: 4px;
|
||||
max-width: 40px;
|
||||
}
|
||||
|
||||
.badge-size-large {
|
||||
padding: 3px 6px;
|
||||
font-size: 9px;
|
||||
border-radius: 10px;
|
||||
max-width: 70px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,694 @@
|
|||
<template>
|
||||
<div
|
||||
class="flip-card"
|
||||
:class="{ 'is-flipped': isFlipped }"
|
||||
@mouseenter="handleCardHover"
|
||||
@mouseleave="handleCardLeave"
|
||||
>
|
||||
<div class="flip-card-inner">
|
||||
<!-- 卡片背面 (左邊的樣式) -->
|
||||
<div class="flip-card-back">
|
||||
<div class="back-content">
|
||||
<h3 class="back-title">{{ streamer.name }}</h3>
|
||||
<p class="greeting">{{ streamer.greeting }}</p>
|
||||
<p class="status">{{ streamer.status }}</p>
|
||||
<p class="availability">{{ streamer.availability }}</p>
|
||||
<p class="personality">{{ streamer.personality }}</p>
|
||||
<p class="promise">{{ streamer.promise }}</p>
|
||||
|
||||
<!-- 主播主頁按鈕 -->
|
||||
<button
|
||||
class="profile-button"
|
||||
@click="handleProfileClick"
|
||||
>
|
||||
去這個主播主頁
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片正面 - 簡潔設計 -->
|
||||
<div class="flip-card-front">
|
||||
<div class="photo-container">
|
||||
<img
|
||||
:src="streamer.photo"
|
||||
:alt="streamer.name"
|
||||
class="streamer-photo"
|
||||
/>
|
||||
|
||||
<!-- 簡潔的覆蓋層 -->
|
||||
<div class="photo-overlay">
|
||||
<!-- 右上角徽章組 -->
|
||||
<div class="badges-container">
|
||||
<StreamerBadge
|
||||
v-for="(badgeCode, index) in streamer.badges.slice(0, 3)"
|
||||
:key="`${streamer.id}-badge-${index}`"
|
||||
:code="badgeCode"
|
||||
size="small"
|
||||
class="streamer-badge"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 底部資訊條 -->
|
||||
<div class="info-bar">
|
||||
<div class="streamer-info">
|
||||
<h3 class="streamer-name">{{ streamer.name }}</h3>
|
||||
<p class="streamer-description">{{ streamer.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 性別和生日資訊 -->
|
||||
<div class="personal-info">
|
||||
<div class="gender-badge" :class="streamer.gender">
|
||||
<span class="gender-icon">{{ streamer.gender === 'female' ? '♀' : '♂' }}</span>
|
||||
<span class="gender-text">{{ streamer.gender === 'female' ? '女' : '男' }}</span>
|
||||
</div>
|
||||
<div class="birthday-info">
|
||||
<span class="birthday-icon">🎂</span>
|
||||
<span class="birthday-text">{{ formatBirthday(streamer.birthday) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-row">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">{{ streamer.fans }}</span>
|
||||
<span class="stat-label">粉絲</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">{{ streamer.orders }}</span>
|
||||
<span class="stat-label">接單</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import StreamerBadge from './StreamerBadge.vue';
|
||||
|
||||
interface Streamer {
|
||||
id: number;
|
||||
name: string;
|
||||
photo: string;
|
||||
description: string;
|
||||
rank: string;
|
||||
fans: number;
|
||||
orders: number;
|
||||
badges: string[];
|
||||
greeting: string;
|
||||
status: string;
|
||||
availability: string;
|
||||
personality: string;
|
||||
promise: string;
|
||||
gender: 'male' | 'female';
|
||||
birthday: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
streamer: Streamer;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<{
|
||||
click: [streamer: Streamer];
|
||||
more: [streamer: Streamer];
|
||||
profile: [streamer: Streamer];
|
||||
}>();
|
||||
|
||||
const isFlipped = ref(false);
|
||||
|
||||
const handleCardHover = () => {
|
||||
isFlipped.value = true;
|
||||
console.log('Card hovered, showing back');
|
||||
};
|
||||
|
||||
const handleCardLeave = () => {
|
||||
isFlipped.value = false;
|
||||
console.log('Card left, showing front');
|
||||
};
|
||||
|
||||
const handleMoreClick = (event: Event) => {
|
||||
event.stopPropagation();
|
||||
emit('more', props.streamer);
|
||||
};
|
||||
|
||||
const handleStreamerClick = (event: Event) => {
|
||||
event.stopPropagation();
|
||||
emit('click', props.streamer);
|
||||
};
|
||||
|
||||
const handleProfileClick = (event: Event) => {
|
||||
event.stopPropagation();
|
||||
emit('profile', props.streamer);
|
||||
};
|
||||
|
||||
// 格式化生日顯示
|
||||
const formatBirthday = (birthday: string): string => {
|
||||
const date = new Date(birthday);
|
||||
return `${date.getMonth() + 1}/${date.getDate()}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 翻轉卡片容器 */
|
||||
.flip-card {
|
||||
width: 100%;
|
||||
height: 320px;
|
||||
min-height: 320px;
|
||||
max-height: 320px;
|
||||
perspective: 1000px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: z-index 0.3s ease;
|
||||
}
|
||||
|
||||
.flip-card:hover {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.flip-card.is-flipped {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.flip-card-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
transition: transform 0.6s;
|
||||
transform-style: preserve-3d;
|
||||
min-height: 320px;
|
||||
max-height: 320px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.flip-card-back,
|
||||
.flip-card-front {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1;
|
||||
/* 確保正面和背面尺寸完全一致 */
|
||||
min-height: 320px;
|
||||
max-height: 320px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 卡片背面樣式 (左邊) */
|
||||
.flip-card-back {
|
||||
background: linear-gradient(135deg, rgba(255, 107, 157, 0.8) 0%, rgba(255, 167, 38, 0.8) 100%);
|
||||
color: white;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* 移除框框效果 */
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.flip-card-back::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.05) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.back-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 50px 25px;
|
||||
box-sizing: border-box;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.back-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 0 0 16px 0;
|
||||
color: white;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.back-content p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
color: white;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.greeting {
|
||||
font-weight: 600;
|
||||
color: #fff3e0;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.status {
|
||||
color: #ffe0b2;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.availability {
|
||||
color: #ffccbc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.personality {
|
||||
color: #f8bbd9;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.promise {
|
||||
color: #e1bee7;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 主播主頁按鈕 */
|
||||
.profile-button {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.4);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 20px;
|
||||
border-radius: 25px;
|
||||
backdrop-filter: blur(8px);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.profile-button:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.profile-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
/* 卡片正面樣式 - 簡潔設計 */
|
||||
.flip-card-front {
|
||||
background: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.photo-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.streamer-photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.photo-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(0, 0, 0, 0.1) 0%,
|
||||
transparent 30%,
|
||||
transparent 70%,
|
||||
rgba(0, 0, 0, 0.7) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* 徽章容器 */
|
||||
.badges-container {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
max-width: 120px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.streamer-badge {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 底部資訊條 */
|
||||
.info-bar {
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.streamer-info {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.streamer-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin: 0 0 4px 0;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.streamer-description {
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* 個人資訊區域 */
|
||||
.personal-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 性別徽章 */
|
||||
.gender-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.gender-badge.female {
|
||||
background: rgba(255, 182, 193, 0.8);
|
||||
color: #d63384;
|
||||
border: 1px solid rgba(214, 51, 132, 0.3);
|
||||
}
|
||||
|
||||
.gender-badge.male {
|
||||
background: rgba(173, 216, 230, 0.8);
|
||||
color: #0d6efd;
|
||||
border: 1px solid rgba(13, 110, 253, 0.3);
|
||||
}
|
||||
|
||||
.gender-icon {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gender-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* 生日資訊 */
|
||||
.birthday-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
backdrop-filter: blur(4px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.birthday-icon {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.birthday-text {
|
||||
font-size: 10px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 統計行 */
|
||||
.stats-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 10px;
|
||||
color: white;
|
||||
margin-top: 2px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* 翻轉狀態樣式 - 懸停時顯示背面(粉橙漸變) */
|
||||
.flip-card.is-flipped .flip-card-back {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flip-card.is-flipped .flip-card-front {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 響應式設計 */
|
||||
@media (max-width: 768px) {
|
||||
.flip-card {
|
||||
height: 300px;
|
||||
min-height: 300px;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.flip-card-inner {
|
||||
min-height: 300px;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.flip-card-back,
|
||||
.flip-card-front {
|
||||
min-height: 300px;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.back-content {
|
||||
padding: 45px 22px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.back-title {
|
||||
font-size: 20px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.back-content p {
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.profile-button {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
margin-top: 16px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* 正面卡片響應式 */
|
||||
.streamer-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.streamer-description {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.personal-info {
|
||||
margin-bottom: 8px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.gender-badge {
|
||||
padding: 3px 6px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.birthday-info {
|
||||
padding: 3px 6px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* 徽章響應式 */
|
||||
.badges-container {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
max-width: 100px;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.flip-card {
|
||||
height: 280px;
|
||||
min-height: 280px;
|
||||
max-height: 280px;
|
||||
}
|
||||
|
||||
.flip-card-inner {
|
||||
min-height: 280px;
|
||||
max-height: 280px;
|
||||
}
|
||||
|
||||
.flip-card-back,
|
||||
.flip-card-front {
|
||||
min-height: 280px;
|
||||
max-height: 280px;
|
||||
}
|
||||
|
||||
.back-content {
|
||||
padding: 35px 18px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.back-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.back-content p {
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.profile-button {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
margin-top: 14px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
/* 正面卡片響應式 */
|
||||
.streamer-name {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.streamer-description {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.personal-info {
|
||||
margin-bottom: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.gender-badge {
|
||||
padding: 2px 5px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.birthday-info {
|
||||
padding: 2px 5px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* 徽章響應式 */
|
||||
.badges-container {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
max-width: 90px;
|
||||
gap: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色主題適配 */
|
||||
.theme-dark .flip-card-front {
|
||||
background: var(--window-background);
|
||||
color: var(--content-text-color);
|
||||
}
|
||||
|
||||
.theme-dark .streamer-name {
|
||||
color: var(--content-text-color);
|
||||
}
|
||||
|
||||
.theme-dark .streamer-description {
|
||||
color: var(--content-text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
|
@ -18,14 +18,15 @@
|
|||
"back": "Back"
|
||||
},
|
||||
"apps": {
|
||||
"livestream-hub": "LiveStream Hub",
|
||||
"livestream-hub": "Latest News",
|
||||
"calculator": "Calculator"
|
||||
},
|
||||
"livestream": {
|
||||
"title": "LiveStream Hub",
|
||||
"subtitle": "Discover amazing live content",
|
||||
"featuredStreams": "Featured Streams",
|
||||
"popularStreamers": "Popular Streamers",
|
||||
"title": "Latest News",
|
||||
"subtitle": "Stay updated with the latest information",
|
||||
"featuredStreams": "Featured News",
|
||||
"popularStreamers": "Popular Updates",
|
||||
"recommendedServices": "Recommended Services",
|
||||
"viewers": "viewers",
|
||||
"live": "Live",
|
||||
"offline": "Offline",
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
"back": "返回"
|
||||
},
|
||||
"apps": {
|
||||
"livestream-hub": "直播中心",
|
||||
"livestream-hub": "最新消息",
|
||||
"calculator": "計算機"
|
||||
},
|
||||
"livestream": {
|
||||
"title": "直播中心",
|
||||
"subtitle": "發現最精彩的直播內容",
|
||||
"featuredStreams": "精選直播",
|
||||
"popularStreamers": "熱門主播",
|
||||
"title": "最新消息",
|
||||
"subtitle": "掌握最新資訊動態",
|
||||
"featuredStreams": "精選消息",
|
||||
"popularStreamers": "熱門資訊",
|
||||
"recommendedServices": "推薦服務",
|
||||
"viewers": "觀看",
|
||||
"live": "直播中",
|
||||
"offline": "離線",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"@nuxt/scripts": "^0.11.13",
|
||||
"@nuxt/test-utils": "^3.19.2",
|
||||
"@nuxt/ui": "^3.3.4",
|
||||
"@nuxtjs/i18n": "^10.1.0",
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
"@unhead/vue": "^2.0.17",
|
||||
"better-sqlite3": "^12.3.0",
|
||||
|
@ -23,9 +24,7 @@
|
|||
"vue": "^3.5.21",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/i18n": "^10.1.0"
|
||||
}
|
||||
"devDependencies": {}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
"version": "5.2.0",
|
||||
|
@ -1426,7 +1425,6 @@
|
|||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-11.0.1.tgz",
|
||||
"integrity": "sha512-5l10G5wE2cQRsZMS9y0oSFMOLW5IG/SgbkIUltqnwF1EMRrRbUAHFiPabXdGTHeexCsMTcxj/1w9i0rzjJU9IQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "^11.1.10",
|
||||
|
@ -1455,14 +1453,12 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@intlify/core": {
|
||||
"version": "11.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core/-/core-11.1.12.tgz",
|
||||
"integrity": "sha512-Uccp4VtalUSk/b4F9nBBs7VGgIh9VnXTSHHQ+Kc0AetsHJLxdi04LfhfSi4dujtsTAWnHMHWZw07UbMm6Umq1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.1.12",
|
||||
|
@ -1479,7 +1475,6 @@
|
|||
"version": "11.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.1.12.tgz",
|
||||
"integrity": "sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "11.1.12",
|
||||
|
@ -1496,7 +1491,6 @@
|
|||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/h3/-/h3-0.7.1.tgz",
|
||||
"integrity": "sha512-D/9+L7IzPrOa7e6R/ztepXayAq+snfzBYIwAk3RbaQsLEXwVNjC5c+WKXjni1boc/plGRegw4/m33SaFwvdEpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core": "^11.0.0",
|
||||
|
@ -1513,7 +1507,6 @@
|
|||
"version": "11.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.12.tgz",
|
||||
"integrity": "sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "11.1.12",
|
||||
|
@ -1530,7 +1523,6 @@
|
|||
"version": "11.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.12.tgz",
|
||||
"integrity": "sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
@ -1543,7 +1535,6 @@
|
|||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-11.0.1.tgz",
|
||||
"integrity": "sha512-nH5NJdNjy/lO6Ne8LDtZzv4SbpVsMhPE+LbvBDmMeIeJDiino8sOJN2QB3MXzTliYTnqe3aB9Fw5+LJ/XVaXCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
|
@ -1581,7 +1572,6 @@
|
|||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/utils/-/utils-0.13.0.tgz",
|
||||
"integrity": "sha512-8i3uRdAxCGzuHwfmHcVjeLQBtysQB2aXl/ojoagDut5/gY5lvWCQ2+cnl2TiqE/fXj/D8EhWG/SLKA7qz4a3QA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
|
@ -1594,7 +1584,6 @@
|
|||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz",
|
||||
"integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.24.6",
|
||||
|
@ -1630,7 +1619,6 @@
|
|||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz",
|
||||
"integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "10.0.8",
|
||||
|
@ -1647,7 +1635,6 @@
|
|||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
|
||||
"integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "10.0.8",
|
||||
|
@ -1664,7 +1651,6 @@
|
|||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz",
|
||||
"integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
@ -1677,14 +1663,12 @@
|
|||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": {
|
||||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz",
|
||||
"integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "10.0.8",
|
||||
|
@ -1858,7 +1842,6 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@miyaneee/rollup-plugin-json5/-/rollup-plugin-json5-1.2.0.tgz",
|
||||
"integrity": "sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
|
@ -2873,7 +2856,6 @@
|
|||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nuxtjs/i18n/-/i18n-10.1.0.tgz",
|
||||
"integrity": "sha512-2h/6Y4ke+mYq3RrV71erTBn1HzKKKPGEJrzYW6GA8SAc91zb7jqyfRkElG95Cei+2+6XJrt73Djys5qTc0tCUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core": "^11.1.11",
|
||||
|
@ -2917,7 +2899,6 @@
|
|||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.1.2.tgz",
|
||||
"integrity": "sha512-P5q41xeEOa6ZQC0PvIP7TSBmOAMxXK4qihDcCbYIJq8RcVsEPbGZVlidmxE6EOw1ucSyodq9nbV31FAKwoL4NQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"c12": "^3.2.0",
|
||||
|
@ -2954,7 +2935,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -2971,7 +2951,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -2988,7 +2967,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3005,7 +2983,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3022,7 +2999,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3039,7 +3015,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3056,7 +3031,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3073,7 +3047,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3090,7 +3063,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3107,7 +3079,6 @@
|
|||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3124,7 +3095,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3141,7 +3111,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3158,7 +3127,6 @@
|
|||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
|
@ -3175,7 +3143,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3192,7 +3159,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3206,7 +3172,6 @@
|
|||
"version": "0.81.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.81.0.tgz",
|
||||
"integrity": "sha512-CnOqkybZK8z6Gx7Wb1qF7AEnSzbol1WwcIzxYOr8e91LytGOjo0wCpgoYWZo8sdbpqX+X+TJayIzo4Pv0R/KjA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/Boshen"
|
||||
|
@ -3219,7 +3184,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3236,7 +3200,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3253,7 +3216,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3270,7 +3232,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3287,7 +3248,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3304,7 +3264,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3321,7 +3280,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3338,7 +3296,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3355,7 +3312,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3372,7 +3328,6 @@
|
|||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3389,7 +3344,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3406,7 +3360,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3423,7 +3376,6 @@
|
|||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
|
@ -3440,7 +3392,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3457,7 +3408,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3471,7 +3421,6 @@
|
|||
"version": "3.0.0-beta.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.0.0-beta.15.tgz",
|
||||
"integrity": "sha512-DMgq/rIh1H20WYNWU7krIbEfJRYDDhy7ix64GlT4AVUJZZWCZ5pxiYVJR3A3GmWQPkn7Pg7i3oIiGqu4JGC65w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-sfc": "^3.5.17",
|
||||
|
@ -3499,14 +3448,12 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz",
|
||||
"integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nuxtjs/i18n/node_modules/oxc-parser": {
|
||||
"version": "0.81.0",
|
||||
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.81.0.tgz",
|
||||
"integrity": "sha512-iceu9s70mZyjKs6V2QX7TURkJj1crnKi9csGByWvOWwrR5rwq0U0f49yIlRAzMP4t7K2gRC1MnyMZggMhiwAVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "^0.81.0"
|
||||
|
@ -3539,7 +3486,6 @@
|
|||
"version": "0.81.0",
|
||||
"resolved": "https://registry.npmjs.org/oxc-transform/-/oxc-transform-0.81.0.tgz",
|
||||
"integrity": "sha512-Sfb7sBZJoA7GPNlgeVvwqSS+fKFG5Lu2N4CJIlKPdkBgMDwVqUPOTVrEXHYaoYilA2x0VXVwLWqjcW3CwrfzSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -3569,7 +3515,6 @@
|
|||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-0.4.0.tgz",
|
||||
"integrity": "sha512-x5TJAZQD3kRnRBGZ+8uryMZUwkTYddwzBftkqyJIcmpBOXmoK/fwriRKATjZroR2d+aS7+2w1B0oz189bBTwfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"estree-walker": "^3.0.3",
|
||||
|
@ -3583,7 +3528,6 @@
|
|||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.14.0.tgz",
|
||||
"integrity": "sha512-ipjunvS5e2aFHBAUFuLbHl2aHKbXXXBhTxGT9wZx66fNVPdEQzVVitF8nODr1plANhTTa3UZ+DQu9uyLngMzoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue-macros/common": "3.0.0-beta.15",
|
||||
|
@ -5021,7 +4965,6 @@
|
|||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz",
|
||||
"integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
|
@ -9103,7 +9046,6 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
||||
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"esprima": "^4.0.1",
|
||||
|
@ -9125,7 +9067,6 @@
|
|||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
|
@ -9614,7 +9555,6 @@
|
|||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
|
@ -11429,7 +11369,6 @@
|
|||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.1.tgz",
|
||||
"integrity": "sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.5.0",
|
||||
|
@ -11448,7 +11387,6 @@
|
|||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
|
||||
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
@ -11461,7 +11399,6 @@
|
|||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
|
||||
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"acorn": "^8.9.0",
|
||||
|
@ -13515,7 +13452,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nuxt-define/-/nuxt-define-1.0.0.tgz",
|
||||
"integrity": "sha512-CYZ2WjU+KCyCDVzjYUM4eEpMF0rkPmkpiFrybTqqQCRpUbPt2h3snswWIpFPXTi+osRCY6Og0W/XLAQgDL4FfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/bobbiegoede"
|
||||
|
@ -16634,7 +16570,6 @@
|
|||
"version": "2.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
|
@ -18056,7 +17991,6 @@
|
|||
"version": "11.1.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.12.tgz",
|
||||
"integrity": "sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.1.12",
|
||||
|
@ -18077,7 +18011,6 @@
|
|||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
|
@ -18368,7 +18301,6 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz",
|
||||
"integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.0.0",
|
||||
|
@ -18385,7 +18317,6 @@
|
|||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
|
||||
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
|
|
@ -36,8 +36,8 @@ export const useAppsStore = defineStore('apps', () => {
|
|||
{
|
||||
id: 'livestream-hub',
|
||||
name: 'livestream-hub', // Use translation key instead of hardcoded name
|
||||
icon: '📺',
|
||||
component: 'LiveStreamHub',
|
||||
icon: '📰',
|
||||
component: 'NewsHub',
|
||||
description: 'Discover and watch live streams from popular streamers',
|
||||
category: 'Entertainment'
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue