feat: finalize feature integration

Complete guild, live, chat, and moments pages with full functionality.

#VERCEL_SKIP

Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
This commit is contained in:
v0
2025-11-24 02:31:53 +00:00
parent a6a974378d
commit 451663a0bd
54 changed files with 2130 additions and 524 deletions

106
app/chat/[id]/page.tsx Normal file
View File

@@ -0,0 +1,106 @@
"use client"
import { useRouter } from "next/navigation"
import { ArrowLeft, Mic, Plus, Video, MoreHorizontal, Users } from "lucide-react"
import Image from "next/image"
export default function ChatRoomPage({ params }: { params: { id: string } }) {
const router = useRouter()
return (
<div className="h-screen bg-black flex flex-col">
{/* Header */}
<div className="px-4 py-3 flex items-center justify-between border-b border-white/10 bg-zinc-900/50 backdrop-blur-md">
<div className="flex items-center gap-3">
<button onClick={() => router.back()} className="active:scale-90 transition-transform">
<ArrowLeft size={20} className="text-white" />
</button>
<div>
<h1 className="font-bold text-sm text-white"> - </h1>
<div className="flex items-center gap-1 text-[10px] text-primary font-bold">
<Users size={10} />
<span>8/10</span>
<span className="text-white/30">|</span>
<span className="text-green-400"></span>
</div>
</div>
</div>
<button className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center">
<MoreHorizontal size={18} className="text-white" />
</button>
</div>
{/* Live Stream Preview within Chat */}
<div
className="w-full bg-zinc-900/80 border-b border-white/5 p-3 flex items-center gap-3 cursor-pointer hover:bg-zinc-800 transition-colors backdrop-blur-sm"
onClick={() => router.push("/live/101")}
>
<div className="w-24 h-14 rounded-lg bg-black relative overflow-hidden border border-white/10">
<Image src="/lol-gameplay.jpg" alt="Live" fill className="object-cover" />
<div className="absolute top-1 left-1 px-1 rounded bg-primary text-[8px] font-bold text-white animate-pulse">
LIVE
</div>
</div>
<div className="flex-1">
<h3 className="text-xs font-bold text-white mb-1"></h3>
<p className="text-[10px] text-primary"> &gt;</p>
</div>
<div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center">
<Video size={16} className="text-white/60" />
</div>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
<div className="text-center text-[10px] text-white/30 my-4">12:30</div>
<div className="flex gap-3">
<div className="w-8 h-8 rounded-full bg-zinc-800 overflow-hidden flex-shrink-0">
<Image src="/avatar-1.jpg" alt="User" width={32} height={32} />
</div>
<div className="max-w-[70%]">
<p className="text-[10px] text-white/50 mb-1 ml-1"></p>
<div className="bg-zinc-800 p-2 rounded-xl rounded-tl-none text-xs text-white/90">
</div>
</div>
</div>
<div className="flex gap-3 flex-row-reverse">
<div className="w-8 h-8 rounded-full bg-zinc-800 overflow-hidden flex-shrink-0">
<Image src="/my-avatar.jpg" alt="User" width={32} height={32} />
</div>
<div className="max-w-[70%] flex flex-col items-end">
<div className="bg-primary p-2 rounded-xl rounded-tr-none text-xs text-white"></div>
</div>
</div>
<div className="flex gap-3">
<div className="w-8 h-8 rounded-full bg-zinc-800 overflow-hidden flex-shrink-0">
<Image src="/avatar-2.jpg" alt="User" width={32} height={32} />
</div>
<div className="max-w-[70%]">
<p className="text-[10px] text-white/50 mb-1 ml-1"></p>
<div className="bg-zinc-800 p-2 rounded-xl rounded-tl-none text-xs text-white/90"> 😂</div>
</div>
</div>
</div>
{/* Input Area */}
<div className="p-3 bg-zinc-900/90 border-t border-white/10 flex items-end gap-3 pb-safe">
<button className="p-2 rounded-full hover:bg-white/5">
<Mic size={20} className="text-white/60" />
</button>
<div className="flex-1 bg-black/50 rounded-2xl min-h-[36px] flex items-center px-3 border border-white/5">
<input
type="text"
className="bg-transparent w-full text-sm text-white placeholder:text-white/30 outline-none py-2"
placeholder="发送消息..."
/>
</div>
<button className="p-2 rounded-full hover:bg-white/5">
<Plus size={20} className="text-white/60" />
</button>
</div>
</div>
)
}

105
app/coupons/page.tsx Normal file
View File

@@ -0,0 +1,105 @@
"use client"
import { ArrowLeft, Ticket, Clock, ChevronRight } from "lucide-react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export default function CouponsPage() {
const router = useRouter()
const coupons = [
{ id: 1, name: "电竞酒店满200减50", type: "酒店", discount: "50", condition: "满200可用", expires: "2025-02-28" },
{ id: 2, name: "网咖通用8折券", type: "网咖", discount: "8折", condition: "无门槛", expires: "2025-03-15" },
{ id: 3, name: "商城满100减20", type: "商城", discount: "20", condition: "满100可用", expires: "2025-02-20" },
]
return (
<div className="min-h-screen pb-24 bg-background">
{/* Header */}
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
<button onClick={() => router.back()} className="p-2 -ml-2 hover:bg-white/5 rounded-lg transition-colors">
<ArrowLeft size={20} />
</button>
<h1 className="font-bold text-lg"></h1>
</header>
<div className="p-4">
<Tabs defaultValue="available" className="w-full">
<TabsList className="w-full bg-white/5 p-1 rounded-xl mb-4">
<TabsTrigger value="available" className="flex-1 text-xs">
使
</TabsTrigger>
<TabsTrigger value="used" className="flex-1 text-xs">
使
</TabsTrigger>
<TabsTrigger value="expired" className="flex-1 text-xs">
</TabsTrigger>
</TabsList>
<TabsContent value="available" className="space-y-3">
{coupons.map((coupon) => (
<div
key={coupon.id}
className="glass-card rounded-xl overflow-hidden border border-primary/20 hover:border-primary/40 transition-colors cursor-pointer"
>
<div className="flex">
<div className="w-28 bg-gradient-to-br from-primary to-purple-600 p-4 flex flex-col items-center justify-center text-white relative">
<Ticket className="absolute top-2 right-2 opacity-20" size={40} />
<div className="text-3xl font-bold z-10">
{coupon.discount.includes("折") ? coupon.discount : `¥${coupon.discount}`}
</div>
<div className="text-[10px] mt-1 z-10">{coupon.type}</div>
</div>
<div className="flex-1 p-3 flex flex-col justify-between">
<div>
<h4 className="font-bold text-sm mb-1">{coupon.name}</h4>
<p className="text-xs text-white/50">{coupon.condition}</p>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-1 text-[10px] text-white/30">
<Clock size={10} />
{coupon.expires}
</div>
<Button size="sm" className="h-6 text-[10px] px-3">
使
</Button>
</div>
</div>
</div>
</div>
))}
</TabsContent>
<TabsContent value="used" className="text-center py-12">
<Ticket className="w-12 h-12 text-white/20 mx-auto mb-3" />
<p className="text-sm text-white/40">使</p>
</TabsContent>
<TabsContent value="expired" className="text-center py-12">
<Ticket className="w-12 h-12 text-white/20 mx-auto mb-3" />
<p className="text-sm text-white/40"></p>
</TabsContent>
</Tabs>
{/* Get More Coupons CTA */}
<div className="mt-6 glass-card p-4 rounded-xl border border-primary/20">
<div className="flex items-center justify-between">
<div>
<h3 className="font-bold text-sm mb-1"></h3>
<p className="text-xs text-white/50"></p>
</div>
<Button
size="sm"
variant="outline"
className="border-primary text-primary hover:bg-primary/10 bg-transparent"
>
<ChevronRight size={14} className="ml-1" />
</Button>
</div>
</div>
</div>
</div>
)
}

152
app/dress-up/page.tsx Normal file
View File

@@ -0,0 +1,152 @@
"use client"
import { ArrowLeft, TrendingUp, ShoppingCart, Sparkles } from "lucide-react"
import { useRouter } from "next/navigation"
import Image from "next/image"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { toast } from "@/hooks/use-toast"
export default function DressUpPage() {
const router = useRouter()
const avatars = [
{ id: 1, name: "赛博朋克战士", price: 98, rarity: "传说", sales: 1205, image: "cyberpunk warrior avatar neon" },
{ id: 2, name: "甜心电竞少女", price: 68, rarity: "稀有", sales: 3420, image: "cute esports girl avatar pink" },
{ id: 3, name: "暗影刺客", price: 128, rarity: "传说", sales: 856, image: "shadow assassin avatar dark" },
{ id: 4, name: "机甲战神", price: 88, rarity: "史诗", sales: 2100, image: "mecha warrior avatar blue" },
]
const decorations = [
{ id: 101, name: "星空边框", price: 38, type: "边框", image: "starry frame decoration" },
{ id: 102, name: "炫光特效", price: 58, type: "特效", image: "neon glow effect" },
{ id: 103, name: "王者徽章", price: 48, type: "徽章", image: "champion badge gold" },
]
return (
<div className="min-h-screen pb-24 bg-background">
{/* Header */}
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
<button onClick={() => router.back()} className="p-2 -ml-2 hover:bg-white/5 rounded-lg transition-colors">
<ArrowLeft size={20} />
</button>
<h1 className="font-bold text-lg"></h1>
<div className="ml-auto flex items-center gap-2 text-xs bg-white/5 px-3 py-1.5 rounded-full">
<Sparkles size={14} className="text-yellow-400" />
<span className="text-white/80"></span>
</div>
</header>
<div className="p-4">
{/* Current Avatar Display */}
<div className="glass-card p-6 rounded-2xl mb-6 text-center border border-primary/20">
<div className="w-24 h-24 mx-auto mb-3 rounded-full border-4 border-primary p-1 relative">
<Image src="/current-user-avatar.png" alt="Current Avatar" fill className="rounded-full object-cover" />
<div className="absolute -bottom-2 left-1/2 -translate-x-1/2 bg-primary text-[10px] px-2 py-0.5 rounded-full text-white font-bold">
LV.8
</div>
</div>
<h3 className="font-bold mb-1"></h3>
<p className="text-xs text-white/50 mb-4"></p>
<Button size="sm" variant="outline" className="border-white/20 hover:bg-white/5 bg-transparent">
</Button>
</div>
{/* Marketplace Tabs */}
<Tabs defaultValue="avatar" className="w-full">
<TabsList className="w-full bg-white/5 p-1 rounded-xl mb-4">
<TabsTrigger value="avatar" className="flex-1 text-xs">
</TabsTrigger>
<TabsTrigger value="decoration" className="flex-1 text-xs">
</TabsTrigger>
<TabsTrigger value="my" className="flex-1 text-xs">
</TabsTrigger>
</TabsList>
<TabsContent value="avatar" className="space-y-3">
<div className="flex items-center justify-between mb-3">
<h3 className="font-bold text-sm"></h3>
<button className="text-xs text-primary">
<TrendingUp size={12} className="inline ml-1" />
</button>
</div>
<div className="grid grid-cols-2 gap-3">
{avatars.map((avatar) => (
<div
key={avatar.id}
className="glass-card p-3 rounded-xl hover:bg-white/10 transition-colors cursor-pointer"
onClick={() =>
toast({
title: "即将购买",
description: `${avatar.name} - ¥${avatar.price}`,
})
}
>
<div className="relative w-full aspect-square rounded-lg overflow-hidden mb-2 bg-zinc-800">
<Image
src={`/.jpg?height=200&width=200&query=${avatar.image}`}
alt={avatar.name}
fill
className="object-cover"
/>
<div className="absolute top-2 right-2 bg-yellow-500/90 text-black text-[9px] px-1.5 py-0.5 rounded font-bold">
{avatar.rarity}
</div>
</div>
<h4 className="font-bold text-sm mb-1 truncate">{avatar.name}</h4>
<div className="flex items-center justify-between text-xs">
<span className="text-white/50">{avatar.sales}</span>
<span className="text-primary font-bold">¥{avatar.price}</span>
</div>
<Button size="sm" className="w-full mt-2 h-7 text-xs bg-primary hover:bg-primary/90">
<ShoppingCart size={12} className="mr-1" />
</Button>
</div>
))}
</div>
</TabsContent>
<TabsContent value="decoration" className="space-y-3">
<div className="grid gap-3">
{decorations.map((item) => (
<div
key={item.id}
className="glass-card p-3 rounded-xl flex items-center gap-3 hover:bg-white/10 transition-colors cursor-pointer"
>
<div className="w-16 h-16 rounded-lg overflow-hidden bg-zinc-800 relative">
<Image
src={`/.jpg?height=100&width=100&query=${item.image}`}
alt={item.name}
fill
className="object-cover"
/>
</div>
<div className="flex-1">
<h4 className="font-bold text-sm mb-1">{item.name}</h4>
<div className="flex items-center gap-2">
<span className="text-[10px] bg-white/10 px-2 py-0.5 rounded text-white/60">{item.type}</span>
<span className="text-primary font-bold text-sm">¥{item.price}</span>
</div>
</div>
<Button size="sm" className="h-8 text-xs">
</Button>
</div>
))}
</div>
</TabsContent>
<TabsContent value="my" className="text-center py-12">
<Sparkles className="w-12 h-12 text-white/20 mx-auto mb-3" />
<p className="text-sm text-white/40"></p>
<p className="text-xs text-white/30 mt-1"></p>
</TabsContent>
</Tabs>
</div>
</div>
)
}

117
app/footprint/page.tsx Normal file
View File

@@ -0,0 +1,117 @@
"use client"
import { ArrowLeft, MapPin, Clock, Star, Heart } from "lucide-react"
import { useRouter } from "next/navigation"
import Image from "next/image"
export default function FootprintPage() {
const router = useRouter()
const history = [
{
date: "2025-01-20",
type: "hotel",
name: "玩值电竞酒店(旗舰店)",
rating: 5,
image: "esports hotel room",
},
{
date: "2025-01-18",
type: "match",
name: "与GM-远洋的粉丝群匹配成功",
detail: "在线聊天2小时",
},
{
date: "2025-01-15",
type: "purchase",
name: "购买了王者荣耀皮肤",
detail: "白龙吟",
},
{
date: "2025-01-10",
type: "cafe",
name: "网鱼网咖(万达店)",
rating: 4,
image: "internet cafe",
},
]
return (
<div className="min-h-screen pb-24 bg-background">
{/* Header */}
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
<button onClick={() => router.back()} className="p-2 -ml-2 hover:bg-white/5 rounded-lg transition-colors">
<ArrowLeft size={20} />
</button>
<h1 className="font-bold text-lg"></h1>
</header>
<div className="p-4">
{/* Stats Overview */}
<div className="grid grid-cols-3 gap-3 mb-6">
<div className="glass-card p-3 rounded-xl text-center">
<div className="text-2xl font-bold text-primary mb-1">12</div>
<div className="text-xs text-white/50">访</div>
</div>
<div className="glass-card p-3 rounded-xl text-center">
<div className="text-2xl font-bold text-green-500 mb-1">8</div>
<div className="text-xs text-white/50">线</div>
</div>
<div className="glass-card p-3 rounded-xl text-center">
<div className="text-2xl font-bold text-yellow-500 mb-1">24</div>
<div className="text-xs text-white/50"></div>
</div>
</div>
{/* Timeline */}
<div className="space-y-4">
{history.map((item, i) => (
<div key={i} className="flex gap-4">
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center text-primary border-2 border-primary">
{item.type === "hotel" && <MapPin size={18} />}
{item.type === "match" && <Heart size={18} />}
{item.type === "purchase" && <Star size={18} />}
{item.type === "cafe" && <MapPin size={18} />}
</div>
{i < history.length - 1 && <div className="w-0.5 flex-1 bg-white/10 mt-2" />}
</div>
<div className="flex-1 pb-4">
<div className="glass-card p-3 rounded-xl">
<div className="flex items-start justify-between mb-2">
<div>
<h4 className="font-bold text-sm mb-1">{item.name}</h4>
{item.detail && <p className="text-xs text-white/50">{item.detail}</p>}
{item.rating && (
<div className="flex items-center gap-1 mt-1">
{Array.from({ length: item.rating }).map((_, j) => (
<Star key={j} size={12} fill="currentColor" className="text-yellow-400" />
))}
</div>
)}
</div>
{item.image && (
<div className="w-14 h-14 rounded-lg overflow-hidden bg-zinc-800 relative flex-shrink-0">
<Image
src={`/.jpg?height=100&width=100&query=${item.image}`}
alt={item.name}
fill
className="object-cover"
/>
</div>
)}
</div>
<div className="flex items-center gap-1 text-[10px] text-white/30">
<Clock size={10} />
{item.date}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
)
}

142
app/guild/[id]/page.tsx Normal file
View File

@@ -0,0 +1,142 @@
"use client"
import { ArrowLeft, Users, Trophy, Star, Shield, CheckCircle2, MessageCircle } from "lucide-react"
import { useRouter } from "next/navigation"
import Image from "next/image"
import { Button } from "@/components/ui/button"
import { toast } from "@/hooks/use-toast"
export default function GuildDetailPage({ params }: { params: { id: string } }) {
const router = useRouter()
const handleApply = () => {
toast({
title: "申请已提交",
description: "公会管理员将在24小时内审核您的申请",
})
}
return (
<div className="min-h-screen bg-background pb-24 relative">
{/* Hero Section */}
<div className="h-48 relative">
<Image src="/esports-tournament-stadium.jpg" alt="Cover" fill className="object-cover" />
<div className="absolute inset-0 bg-gradient-to-b from-transparent to-background" />
<button
onClick={() => router.back()}
className="absolute top-safe left-4 p-2 rounded-full bg-black/20 backdrop-blur-md text-white z-10"
>
<ArrowLeft size={20} />
</button>
</div>
<div className="px-4 -mt-12 relative z-10">
<div className="flex items-end justify-between mb-4">
<div className="w-24 h-24 rounded-2xl border-4 border-background bg-muted relative overflow-hidden shadow-xl">
<Image src="/esports-team-logo.jpg" alt="Logo" fill className="object-cover" />
</div>
<div className="flex gap-2 mb-2">
<Button size="sm" variant="outline" className="h-8 text-xs gap-1 bg-transparent">
<MessageCircle size={14} />
</Button>
<Button size="sm" className="h-8 text-xs bg-primary gap-1" onClick={handleApply}>
<CheckCircle2 size={14} />
</Button>
</div>
</div>
<div className="mb-6">
<div className="flex items-center gap-2 mb-1">
<h1 className="text-2xl font-bold"></h1>
<span className="px-2 py-0.5 rounded bg-yellow-500/20 text-yellow-400 text-[10px] border border-yellow-500/30 font-bold">
LV.10
</span>
</div>
<p className="text-sm text-white/60 mb-3">
</p>
<div className="flex items-center gap-4 text-xs text-white/40">
<span className="flex items-center gap-1">
<Users size={12} />
1,205
</span>
<span className="flex items-center gap-1">
<Trophy size={12} />
1
</span>
<span className="flex items-center gap-1">
<Star size={12} />
4.9
</span>
</div>
</div>
{/* Benefits */}
<div className="space-y-4">
<h3 className="font-bold text-base flex items-center gap-2">
<Shield size={16} className="text-primary" />
</h3>
<div className="grid grid-cols-2 gap-3">
<div className="glass-card p-3 rounded-xl border-l-2 border-primary">
<div className="text-sm font-bold mb-1"></div>
<div className="text-xs text-white/50">耀/LOL职业教练一对一指导</div>
</div>
<div className="glass-card p-3 rounded-xl border-l-2 border-secondary">
<div className="text-sm font-bold mb-1"></div>
<div className="text-xs text-white/50">50%</div>
</div>
<div className="glass-card p-3 rounded-xl border-l-2 border-accent">
<div className="text-sm font-bold mb-1"></div>
<div className="text-xs text-white/50"></div>
</div>
<div className="glass-card p-3 rounded-xl border-l-2 border-green-500">
<div className="text-sm font-bold mb-1">线</div>
<div className="text-xs text-white/50">/5000</div>
</div>
</div>
</div>
{/* Requirements */}
<div className="space-y-4 mt-6">
<h3 className="font-bold text-base flex items-center gap-2">
<CheckCircle2 size={16} className="text-primary" />
</h3>
<div className="glass-card p-4 rounded-xl space-y-3">
<div className="flex items-start gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-primary mt-1.5" />
<div className="flex-1 text-xs text-white/70">
<span className="text-white font-bold"></span>
耀耀50 / LOL大师以上 /
</div>
</div>
<div className="flex items-start gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-primary mt-1.5" />
<div className="flex-1 text-xs text-white/70">
<span className="text-white font-bold"></span>
</div>
</div>
<div className="flex items-start gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-primary mt-1.5" />
<div className="flex-1 text-xs text-white/70">
<span className="text-white font-bold"></span>
//
</div>
</div>
<div className="flex items-start gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-primary mt-1.5" />
<div className="flex-1 text-xs text-white/70">
<span className="text-white font-bold">线</span>
线4
</div>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -109,7 +109,7 @@ export default function GuildPage() {
<div className="relative">
<div className="w-16 h-16 rounded-lg bg-muted relative overflow-hidden">
<Image
src={`/.png?height=100&width=100&query=${guild.logo}`}
src={`/.jpg?height=100&width=100&query=${guild.logo}`}
alt={guild.name}
fill
className="object-cover"
@@ -157,7 +157,7 @@ export default function GuildPage() {
size="sm"
variant="outline"
className="flex-1 h-8 text-xs border-white/10 hover:bg-white/5 bg-transparent"
onClick={() => toast({ title: "即将开放", description: "公会详情页面正在开发中" })}
onClick={() => router.push(`/guild/${guild.name}`)}
>
<MessageCircle size={14} className="mr-1" />

3
app/hotel/loading.tsx Normal file
View File

@@ -0,0 +1,3 @@
export default function Loading() {
return null
}

234
app/hotel/page.tsx Normal file
View File

@@ -0,0 +1,234 @@
"use client"
import { useState } from "react"
import { Search, MapPin, Filter, Star, ArrowLeft, ChevronDown } from "lucide-react"
import { useRouter } from "next/navigation"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { toast } from "@/hooks/use-toast"
const hotels = [
{
id: 1,
name: "玩值电竞酒店(旗舰店)",
rating: 4.9,
reviews: 1205,
price: 398,
distance: "0.8km",
tags: ["RTX4090", "电竞椅", "24h餐饮"],
district: "西湖区/黄龙体育中心",
},
{
id: 2,
name: "极客空间电竞公寓",
rating: 4.7,
reviews: 856,
price: 268,
distance: "1.2km",
tags: ["五黑房", "投影", "免费停车"],
district: "拱墅区/万达广场",
},
{
id: 3,
name: "异次元电竞酒店",
rating: 4.8,
reviews: 2341,
price: 328,
distance: "2.5km",
tags: ["情侣房", "PS5", "VR体验"],
district: "滨江区/网易园区",
},
]
const cafes = [
{
id: 101,
name: "网鱼网咖(万达店)",
rating: 4.8,
reviews: 5620,
price: 12,
distance: "0.5km",
tags: ["3080显卡", "特权公馆", "现磨咖啡"],
district: "武林广场商圈",
},
{
id: 102,
name: "杰拉电竞馆",
rating: 4.6,
reviews: 3210,
price: 10,
distance: "1.8km",
tags: ["赛事级配置", "美女陪玩", "独立包间"],
district: "城西银泰",
},
]
export default function HotelPage() {
const router = useRouter()
const [activeTab, setActiveTab] = useState("hotel")
const handleBook = (name: string) => {
toast({
title: "预订成功",
description: `您已成功预订 ${name}`,
})
}
return (
<div className="min-h-screen bg-background pb-24">
{/* Header with Search */}
<div className="sticky top-0 z-40 bg-background/95 backdrop-blur-md border-b border-white/5 supports-[backdrop-filter]:bg-background/60">
<div className="flex items-center gap-3 px-4 py-2">
<button onClick={() => router.back()} className="active:scale-90 transition-transform">
<ArrowLeft size={22} />
</button>
<div className="flex-1 h-9 bg-zinc-800/50 rounded-full flex items-center px-3 gap-2 border border-white/5">
<Search size={14} className="text-white/40" />
<input
type="text"
placeholder="搜索酒店、网咖、商圈"
className="bg-transparent border-none outline-none text-xs w-full h-full text-white placeholder:text-white/30"
/>
</div>
<div className="flex items-center gap-0.5 text-white/80">
<MapPin size={18} />
<span className="text-xs font-bold"></span>
</div>
</div>
{/* Tab Switcher & Filters */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<div className="px-4 pb-2">
<TabsList className="grid w-full grid-cols-2 bg-zinc-800/50 h-9">
<TabsTrigger
value="hotel"
className="text-xs data-[state=active]:bg-primary data-[state=active]:text-white"
>
</TabsTrigger>
<TabsTrigger
value="cafe"
className="text-xs data-[state=active]:bg-primary data-[state=active]:text-white"
>
</TabsTrigger>
</TabsList>
</div>
<div className="flex items-center justify-between px-4 py-2 border-t border-white/5">
<div className="flex gap-6 text-xs text-white/60 font-medium">
<button className="flex items-center gap-1 text-primary">
<ChevronDown size={10} />
</button>
<button className="flex items-center gap-1">
<ChevronDown size={10} />
</button>
<button className="flex items-center gap-1">
<ChevronDown size={10} />
</button>
<button className="flex items-center gap-1">
<Filter size={10} />
</button>
</div>
</div>
<TabsContent value="hotel" className="mt-0 px-4 py-2 space-y-2 min-h-[50vh]">
{hotels.map((hotel) => (
<div
key={hotel.id}
className="flex flex-col py-3 border-b border-white/5 last:border-0 cursor-pointer active:bg-white/5 transition-colors"
onClick={() => handleBook(hotel.name)}
>
<div className="flex items-start justify-between mb-2">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold text-base">{hotel.name}</h3>
{hotel.id === 1 && (
<span className="text-[10px] bg-red-500/20 text-red-400 px-1.5 py-0.5 rounded border border-red-500/30">
</span>
)}
</div>
<div className="flex items-center gap-2 text-xs text-white/60 mb-1">
<div className="flex items-center text-yellow-400 font-bold">
<Star size={11} fill="currentColor" />
<span className="ml-0.5">{hotel.rating}</span>
</div>
<span className="text-white/30">|</span>
<span>{hotel.reviews}</span>
<span className="text-white/30">|</span>
<span className="text-primary">{hotel.distance}</span>
</div>
<div className="text-xs text-white/40 mb-2">{hotel.district}</div>
</div>
<div className="text-right">
<div className="flex items-baseline gap-0.5 text-red-500 mb-1">
<span className="text-xs font-bold">¥</span>
<span className="text-2xl font-bold leading-none">{hotel.price}</span>
</div>
<button className="px-3 py-1 bg-gradient-to-r from-amber-500 to-yellow-600 text-white text-xs rounded-md font-bold active:scale-95 transition-transform">
</button>
</div>
</div>
<div className="flex flex-wrap gap-1.5">
{hotel.tags.map((tag, i) => (
<span
key={i}
className="px-2 py-0.5 rounded text-[11px] border border-white/10 text-white/50 bg-white/5"
>
{tag}
</span>
))}
</div>
</div>
))}
</TabsContent>
<TabsContent value="cafe" className="mt-0 px-4 py-2 space-y-2 min-h-[50vh]">
{cafes.map((cafe) => (
<div
key={cafe.id}
className="flex flex-col py-3 border-b border-white/5 last:border-0 cursor-pointer active:bg-white/5 transition-colors"
onClick={() => handleBook(cafe.name)}
>
<div className="flex items-start justify-between mb-2">
<div className="flex-1">
<h3 className="font-bold text-base mb-1">{cafe.name}</h3>
<div className="flex items-center gap-2 text-xs text-white/60 mb-1">
<div className="flex items-center text-yellow-400 font-bold">
<Star size={11} fill="currentColor" />
<span className="ml-0.5">{cafe.rating}</span>
</div>
<span className="text-white/30">|</span>
<span> ¥{(cafe.price * 3).toFixed(0)}</span>
<span className="text-white/30">|</span>
<span className="text-primary">{cafe.distance}</span>
</div>
<div className="text-xs text-white/40 mb-2">{cafe.district}</div>
</div>
<div className="text-right">
<div className="flex items-baseline gap-0.5 mb-1">
<span className="text-xs text-white/60">¥</span>
<span className="text-xl font-bold text-primary leading-none">{cafe.price}</span>
<span className="text-xs text-white/40">/</span>
</div>
</div>
</div>
<div className="flex gap-1.5">
{cafe.tags.map((tag, i) => (
<span
key={i}
className="text-[11px] px-2 py-0.5 rounded bg-primary/10 text-primary border border-primary/20"
>
{tag}
</span>
))}
</div>
</div>
))}
</TabsContent>
</Tabs>
</div>
</div>
)
}

110
app/live/[id]/page.tsx Normal file
View File

@@ -0,0 +1,110 @@
"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { ArrowLeft, Heart, Share2, Gift, Send, MoreHorizontal } from "lucide-react"
import Image from "next/image"
export default function LiveRoomPage({ params }: { params: { id: string } }) {
const router = useRouter()
const [comment, setComment] = useState("")
const [isFollowed, setIsFollowed] = useState(false)
return (
<div className="h-screen w-full bg-black relative overflow-hidden flex flex-col">
{/* Background Live Stream */}
<div className="absolute inset-0 z-0">
<Image src="/esports-live-stream-gameplay.jpg" alt="Live" fill className="object-cover" />
<div className="absolute inset-0 bg-gradient-to-b from-black/30 via-transparent to-black/60" />
</div>
{/* Header */}
<div className="relative z-20 pt-safe px-4 py-2 flex items-center justify-between">
<div className="flex items-center gap-2 bg-black/20 backdrop-blur-md rounded-full p-1 pr-3">
<button onClick={() => router.back()} className="p-1">
<ArrowLeft size={20} className="text-white" />
</button>
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full border border-white/20 overflow-hidden">
<Image src="/gamer-avatar.jpg" alt="Avatar" width={32} height={32} />
</div>
<div>
<h3 className="text-xs font-bold text-white">GM-</h3>
<p className="text-[10px] text-white/70">12.5w观看</p>
</div>
</div>
<button
className={`ml-2 px-3 py-1 rounded-full text-xs font-bold transition-colors ${isFollowed ? "bg-white/10 text-white/50" : "bg-primary text-white"}`}
onClick={() => setIsFollowed(!isFollowed)}
>
{isFollowed ? "已关注" : "关注"}
</button>
</div>
<div className="flex items-center gap-3">
<div className="flex -space-x-2">
{[1, 2, 3].map((i) => (
<div key={i} className="w-8 h-8 rounded-full border border-white/10 overflow-hidden">
<Image
src={`/avatar-.jpg?height=32&width=32&query=avatar_${i}`}
alt="Viewer"
width={32}
height={32}
/>
</div>
))}
</div>
<button className="w-8 h-8 rounded-full bg-black/20 backdrop-blur-md flex items-center justify-center">
<MoreHorizontal size={18} className="text-white" />
</button>
</div>
</div>
{/* Main Content Spacer */}
<div className="flex-1" />
{/* Chat Area */}
<div className="relative z-20 px-4 pb-2 space-y-2 max-h-[30vh] overflow-y-auto mask-image-t">
<div className="flex items-start gap-2">
<span className="bg-yellow-500/80 px-1.5 rounded text-[10px] font-bold text-black mt-0.5">LV.12</span>
<p className="text-sm text-white/90">
<span className="text-blue-400 font-bold opacity-80">:</span> 666
</p>
</div>
<div className="flex items-start gap-2">
<span className="bg-primary/80 px-1.5 rounded text-[10px] font-bold text-white mt-0.5">LV.5</span>
<p className="text-sm text-white/90">
<span className="text-blue-400 font-bold opacity-80">:</span> BGM
</p>
</div>
<div className="flex items-start gap-2">
<span className="bg-purple-500/80 px-1.5 rounded text-[10px] font-bold text-white mt-0.5"></span>
<p className="text-sm text-yellow-400 font-bold">系统公告: 严禁违规发言</p>
</div>
</div>
{/* Bottom Actions */}
<div className="relative z-20 px-4 pb-6 pt-2 flex items-center gap-3">
<div className="flex-1 h-10 bg-black/40 backdrop-blur-md rounded-full flex items-center px-4 border border-white/10">
<input
type="text"
placeholder="说点什么..."
className="bg-transparent w-full text-sm text-white placeholder:text-white/50 outline-none"
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
<Send size={18} className="text-white/50" />
</div>
<button className="p-2.5 rounded-full bg-black/40 backdrop-blur-md hover:bg-white/10 transition-colors">
<Gift size={22} className="text-pink-500" />
</button>
<button className="p-2.5 rounded-full bg-black/40 backdrop-blur-md hover:bg-white/10 transition-colors">
<Share2 size={22} className="text-white" />
</button>
<button className="p-2.5 rounded-full bg-primary hover:bg-primary/90 transition-colors shadow-lg shadow-primary/30">
<Heart size={22} className="text-white fill-white" />
</button>
</div>
</div>
)
}

View File

@@ -12,13 +12,14 @@ import {
GraduationCap,
Monitor,
Hotel,
MapPin,
Coffee,
Trophy,
User,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useAppContext } from "@/components/providers/app-provider"
import { toast } from "@/hooks/use-toast"
import Link from "next/link" // Import Link
export default function MallPage() {
const { wallet, pay, recharge } = useAppContext()
@@ -72,19 +73,67 @@ export default function MallPage() {
</h3>
<div className="grid grid-cols-4 gap-3">
{[
{ name: "知识付费", icon: BookOpen, color: "text-blue-400", bg: "bg-blue-500/10", desc: "大神课程" },
{ name: "陪玩课程", icon: GraduationCap, color: "text-pink-400", bg: "bg-pink-500/10", desc: "专业指导" },
{ name: "电竞外设", icon: Monitor, color: "text-purple-400", bg: "bg-purple-500/10", desc: "职业装备" },
{ name: "电竞酒店", icon: Hotel, color: "text-orange-400", bg: "bg-orange-500/10", desc: "订阅特权" },
{ name: "附近网吧", icon: Coffee, color: "text-cyan-400", bg: "bg-cyan-500/10", desc: "上网优惠" },
{ name: "欢乐之路", icon: MapPin, color: "text-green-400", bg: "bg-green-500/10", desc: "趣味闯关" },
{ name: "网咖特权", icon: Coffee, color: "text-teal-400", bg: "bg-teal-500/10", desc: "会员专享" },
{ name: "更多服务", icon: Sparkles, color: "text-yellow-400", bg: "bg-yellow-500/10", desc: "敬请期待" },
{
name: "知识付费",
icon: BookOpen,
color: "text-blue-400",
bg: "bg-blue-500/10",
href: "/my-courses",
},
{
name: "陪玩课程",
icon: GraduationCap,
color: "text-pink-400",
bg: "bg-pink-500/10",
href: "/creator/apply",
},
{
name: "电竞外设",
icon: Monitor,
color: "text-purple-400",
bg: "bg-purple-500/10",
href: "/mall/gear",
},
{
name: "电竞酒店",
icon: Hotel,
color: "text-orange-400",
bg: "bg-orange-500/10",
href: "/hotel",
},
{
name: "附近网吧",
icon: Coffee,
color: "text-cyan-400",
bg: "bg-cyan-500/10",
href: "/hotel",
},
{
name: "游戏代练",
icon: Trophy,
color: "text-yellow-400",
bg: "bg-yellow-500/10",
href: "/services/leveling",
},
{
name: "账号交易",
icon: User,
color: "text-green-400",
bg: "bg-green-500/10",
href: "/services/account",
},
{
name: "更多服务",
icon: Sparkles,
color: "text-white/40",
bg: "bg-white/5",
href: "#",
},
].map((item, i) => (
<div
<Link
key={i}
className="glass-card p-2 rounded-xl flex flex-col items-center gap-2 cursor-pointer hover:bg-white/10 transition-colors"
onClick={() => toast({ title: "即将开放", description: `${item.name}板块正在筹备中` })}
href={item.href}
className="glass-card p-3 rounded-xl flex flex-col items-center gap-2 cursor-pointer hover:bg-white/10 transition-colors"
>
<div className={`w-10 h-10 rounded-full ${item.bg} flex items-center justify-center`}>
<item.icon className={item.color} size={20} />
@@ -92,7 +141,7 @@ export default function MallPage() {
<div className="text-center">
<h3 className="text-[10px] font-bold whitespace-nowrap">{item.name}</h3>
</div>
</div>
</Link>
))}
</div>
</div>

View File

@@ -0,0 +1,69 @@
"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { ChevronLeft, ImageIcon, Hash, MapPin, Smile } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
import { toast } from "@/hooks/use-toast"
export default function CreateMomentPage() {
const router = useRouter()
const [content, setContent] = useState("")
const handlePublish = () => {
if (!content.trim()) {
toast({ title: "内容不能为空", variant: "destructive" })
return
}
toast({ title: "发布成功", description: "您的动态已发布" })
setTimeout(() => router.back(), 1000)
}
return (
<div className="min-h-screen bg-background text-foreground pb-20">
<header className="sticky top-0 z-10 bg-background/80 backdrop-blur-md px-4 py-3 flex items-center justify-between border-b border-white/5">
<Button variant="ghost" size="icon" onClick={() => router.back()}>
<ChevronLeft className="w-6 h-6" />
</Button>
<h1 className="font-bold text-lg"></h1>
<Button size="sm" className="bg-primary hover:bg-primary/90 rounded-full px-4" onClick={handlePublish}>
</Button>
</header>
<div className="p-4 space-y-4">
<Textarea
placeholder="分享你的电竞生活..."
className="min-h-[200px] bg-transparent border-none text-lg resize-none focus-visible:ring-0 p-0 placeholder:text-muted-foreground/50"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
{/* Media Preview Area (Mock) */}
<div className="grid grid-cols-3 gap-2">
<div className="aspect-square rounded-xl bg-white/5 border border-white/10 flex flex-col items-center justify-center cursor-pointer hover:bg-white/10 transition-colors">
<ImageIcon className="w-8 h-8 text-muted-foreground mb-2" />
<span className="text-xs text-muted-foreground">/</span>
</div>
</div>
{/* Toolbar */}
<div className="pt-4 border-t border-white/5 space-y-0">
<Button variant="ghost" className="w-full justify-start gap-3 text-muted-foreground hover:text-white px-2">
<Hash className="w-5 h-5" />
<span></span>
</Button>
<Button variant="ghost" className="w-full justify-start gap-3 text-muted-foreground hover:text-white px-2">
<MapPin className="w-5 h-5" />
<span></span>
</Button>
<Button variant="ghost" className="w-full justify-start gap-3 text-muted-foreground hover:text-white px-2">
<Smile className="w-5 h-5" />
<span>/</span>
</Button>
</div>
</div>
</div>
)
}

3
app/moments/loading.tsx Normal file
View File

@@ -0,0 +1,3 @@
export default function Loading() {
return null
}

View File

@@ -1,17 +1,57 @@
"use client" // Convert to client component
import { Heart, MessageCircle, Share2, Gift, Camera, Play, BookOpen } from "lucide-react"
import { Trophy, Search, Heart, MessageCircle, Share2, RefreshCcw } from "lucide-react"
import Image from "next/image"
import Link from "next/link"
import { toast } from "@/hooks/use-toast" // Import toast
import { useState } from "react" // Added state
const liveStreams = [
{
id: 101,
user: "GM-远洋",
title: "国服第一盲僧 冲分教学",
cover: "league_of_legends_gameplay",
avatar: "gamer_boy_avatar",
viewers: "12.5w",
location: "杭州",
},
{
id: 102,
user: "小柠檬",
title: "人皮话多 甜美声优在线",
cover: "cute_gamer_girl_streaming",
avatar: "gamer_girl_avatar",
viewers: "8.2k",
location: "上海",
},
{
id: 103,
user: "魔兽老张",
title: "ICC 25H全通团 缺强力DPS",
cover: "world_of_warcraft_raid",
avatar: "orc_avatar",
viewers: "5.6k",
location: "北京",
},
{
id: 104,
user: "阿伟刚枪",
title: "绝地求生 单人四排",
cover: "pubg_gameplay_intense",
avatar: "fps_gamer_avatar",
viewers: "3.4w",
location: "成都",
},
]
const moments = [
{
id: 1,
user: "GM-远洋",
avatar: "/gamer-girl-headphones.jpg",
avatar: "/gamer-girl.jpg",
desc: "可惜不是你喜欢的甜妹音 😷 #电竞少女 #英雄联盟 #日常",
video: "/esports-gamer-girl-streaming-vertical.jpg",
video: "/esports-streaming-vertical.jpg",
likes: "1.2w",
comments: 458,
isLive: true,
@@ -21,9 +61,9 @@ const moments = [
{
id: 2,
user: "卡若",
avatar: "/gamer-boy-esports-jersey.jpg",
avatar: "/pro-gamer-male.jpg",
desc: "中单刺客教学,这波操作你学会了吗? #英雄联盟 #教学",
video: "/esports-tournament-stadium-neon-lights-crowd.jpg",
video: "/lol-gameplay-highlight.jpg",
likes: "8.5k",
comments: 230,
isLive: false,
@@ -33,9 +73,9 @@ const moments = [
{
id: 3,
user: "魔兽老张",
avatar: "/placeholder.svg?key=wow",
avatar: "/wow-player.jpg",
desc: "ICC 25人H 教授打法细节讲解 #魔兽世界 #WLK",
video: "/wow-bg.jpg",
video: "/placeholder.svg?height=600&width=400",
likes: "3.2k",
comments: 120,
isLive: false,
@@ -44,127 +84,187 @@ const moments = [
]
export default function MomentsPage() {
const [activeTab, setActiveTab] = useState("live")
const handleLike = () => toast({ title: "点赞成功", description: "已添加到我喜欢的视频" })
const handleShare = () => toast({ title: "分享成功", description: "链接已复制到剪贴板" })
const handleGift = () => toast({ title: "礼物发送成功", description: "主播已收到您的心意" })
const handleRefresh = () => {
toast({ title: "刷新成功", description: "已为您推荐新的热门直播" })
}
return (
<div className="min-h-screen bg-black text-white pb-20">
{/* Top Overlay */}
<div className="sticky top-0 z-30 pt-safe px-4 py-3 flex justify-between items-center bg-black/80 backdrop-blur-md">
<div className="flex gap-4 text-lg font-medium">
<span
className="text-white/60 cursor-pointer hover:text-white"
onClick={() => toast({ title: "关注列表", description: "暂无关注的主播" })}
<div className="h-screen bg-black text-white pb-20 flex flex-col">
{/* Top Navigation */}
<div className="fixed top-0 left-0 right-0 z-30 pt-safe px-4 py-3 bg-gradient-to-b from-black/80 to-transparent">
<div className="flex items-center justify-center gap-6 text-lg font-bold shadow-sm">
<button
onClick={() => setActiveTab("follow")}
className={`${activeTab === "follow" ? "text-white scale-110" : "text-white/50"} transition-all`}
>
</span>
<span className="text-white font-bold border-b-2 border-primary pb-1 cursor-pointer"></span>{" "}
{/* Renamed from 推荐 to 视频推荐 */}
<span
className="text-white/60 cursor-pointer hover:text-white"
onClick={() => toast({ title: "最新视频", description: "已为您刷新最新内容" })}
</button>
<button
onClick={() => setActiveTab("live")}
className={`${activeTab === "live" ? "text-white scale-110" : "text-white/50"} transition-all`}
>
</span>
</button>
<button
onClick={() => setActiveTab("video")}
className={`${activeTab === "video" ? "text-white scale-110" : "text-white/50"} transition-all`}
>
</button>
</div>
<Camera className="text-white/80 cursor-pointer hover:text-primary transition-colors" />
<Search className="absolute right-4 top-4 text-white/80 w-6 h-6" />
</div>
<div className="flex flex-col gap-1">
{moments.map((moment) => (
<div key={moment.id} className="relative w-full aspect-[9/16] bg-zinc-900 shrink-0 snap-start">
<Image src={moment.video || "/placeholder.svg"} alt="Stream" fill className="object-cover opacity-80" />
{/* Play Button Overlay (Mock) */}
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<Play size={48} className="text-white/30 fill-white/30" />
{/* Main Content Area - Scroll Snap */}
<div className="flex-1 overflow-y-auto snap-y snap-mandatory no-scrollbar relative">
{activeTab === "live" && (
<div className="min-h-full pb-20">
<div className="flex items-center justify-center py-4 text-white/30 gap-2 text-xs">
<RefreshCcw size={12} />
<span></span>
</div>
{/* Right Sidebar Actions */}
<div className="absolute right-2 bottom-24 flex flex-col items-center gap-6 z-20">
<Link href={`/star/${moment.id}`} className="relative cursor-pointer">
<div className="w-10 h-10 rounded-full border-2 border-white overflow-hidden">
<div className="grid grid-cols-2 gap-1 p-1">
{liveStreams.map((stream) => (
<Link
href={`/live/${stream.id}`}
key={stream.id}
className="relative aspect-[9/16] rounded-xl overflow-hidden group bg-zinc-900"
>
<Image
src={moment.avatar || "/placeholder.svg"}
alt={moment.user}
width={40}
height={40}
className="object-cover"
src={`/.jpg?key=hs14o&height=640&width=360&query=${stream.cover}`}
alt={stream.title}
fill
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
</div>
<div className="absolute -bottom-2 left-1/2 -translate-x-1/2 w-4 h-4 bg-secondary rounded-full flex items-center justify-center text-[10px] font-bold">
+
</div>
</Link>
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-black/80" />
<button
className="flex flex-col items-center gap-1 group"
onClick={handleLike} // Add click handler
>
<Heart size={28} className="text-white drop-shadow-lg group-hover:text-red-500 transition-colors" />
<span className="text-xs font-medium drop-shadow-md">{moment.likes}</span>
</button>
<Link href={`/moments/${moment.id}`} className="flex flex-col items-center gap-1 group">
<MessageCircle
size={28}
className="text-white drop-shadow-lg group-hover:text-primary transition-colors"
/>
<span className="text-xs font-medium drop-shadow-md">{moment.comments}</span>
</Link>
<button
className="flex flex-col items-center gap-1 group"
onClick={handleShare} // Add click handler
>
<Share2 size={28} className="text-white drop-shadow-lg group-hover:text-green-400 transition-colors" />
<span className="text-xs font-medium drop-shadow-md"></span>
</button>
<button
className="flex flex-col items-center gap-1 group"
onClick={handleGift} // Add click handler
>
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-secondary to-accent flex items-center justify-center animate-spin-slow group-hover:scale-110 transition-transform">
<Gift size={20} />
</div>
<span className="text-xs font-medium text-secondary drop-shadow-md"></span>
</button>
{moment.hasCourse && (
<Link href={`/course/${moment.id}`} className="flex flex-col items-center gap-1 group">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-blue-500 to-cyan-500 flex items-center justify-center group-hover:scale-110 transition-transform">
<BookOpen size={20} className="text-white" />
{/* Live Badge */}
<div className="absolute top-2 left-2 px-2 py-0.5 rounded-full bg-primary/90 backdrop-blur-sm flex items-center gap-1 border border-white/10">
<div className="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
<span className="text-[10px] font-bold">LIVE</span>
</div>
<span className="text-xs font-medium text-blue-400 drop-shadow-md"></span>
</Link>
)}
</div>
{/* Bottom Info */}
<div className="absolute left-0 bottom-10 w-3/4 p-4 z-20">
<div className="flex items-center gap-2 mb-2">
<span className="bg-white/20 backdrop-blur-md px-2 py-0.5 rounded text-[10px]"></span>
<Link href={`/star/${moment.id}`} className="font-bold text-shadow-sm hover:underline">
@{moment.user}
</Link>
</div>
<p className="text-sm text-white/90 leading-relaxed text-shadow-sm line-clamp-2">{moment.desc}</p>
{moment.isLive && (
<div className="mt-2 flex items-center gap-2 text-xs text-white/60">
<span className="w-3 h-3 bg-primary/50 rounded-full animate-pulse" />
<span></span>
</div>
)}
</div>
<div className="absolute top-2 right-2 text-[10px] bg-black/40 px-1.5 py-0.5 rounded text-white/80 backdrop-blur-sm">
{stream.location}
</div>
{/* Gradient Overlay */}
<div className="absolute inset-x-0 bottom-0 h-64 bg-gradient-to-t from-black/90 via-black/40 to-transparent pointer-events-none" />
<div className="absolute bottom-0 left-0 w-full p-3">
<div className="flex items-center gap-2 mb-2">
<div className="w-6 h-6 rounded-full border border-white/50 overflow-hidden relative">
<Image
src={`/.jpg?key=4ej4r&height=50&width=50&query=${stream.avatar}`}
alt={stream.user}
fill
className="object-cover"
/>
</div>
<span className="text-xs font-bold truncate">{stream.user}</span>
</div>
<h3 className="text-sm leading-tight line-clamp-2 mb-1 text-white/90">{stream.title}</h3>
<span className="text-[10px] text-primary">{stream.viewers}</span>
</div>
</Link>
))}
{/* Repeat specifically to show scrolling content */}
{liveStreams.map((stream) => (
<Link
href={`/live/${stream.id}`}
key={`repeat-${stream.id}`}
className="relative aspect-[9/16] rounded-xl overflow-hidden group bg-zinc-900"
>
<Image
src={`/.jpg?key=u09oe&height=640&width=360&query=${stream.cover} 2`}
alt={stream.title}
fill
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-black/80" />
<div className="absolute top-2 left-2 px-2 py-0.5 rounded-full bg-primary/90 backdrop-blur-sm flex items-center gap-1 border border-white/10">
<div className="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
<span className="text-[10px] font-bold">LIVE</span>
</div>
<div className="absolute bottom-0 left-0 w-full p-3">
<div className="flex items-center gap-2 mb-2">
<div className="w-6 h-6 rounded-full border border-white/50 overflow-hidden relative">
<Image
src={`/.jpg?key=t53r6&height=50&width=50&query=${stream.avatar}`}
alt={stream.user}
fill
className="object-cover"
/>
</div>
<span className="text-xs font-bold truncate">{stream.user}</span>
</div>
<h3 className="text-sm leading-tight line-clamp-2 mb-1 text-white/90">{stream.title}</h3>
<span className="text-[10px] text-primary">{stream.viewers}</span>
</div>
</Link>
))}
</div>
</div>
))}
)}
<div className="py-8 text-center text-xs text-white/30"></div>
{activeTab === "video" && (
<div className="h-full snap-y snap-mandatory overflow-y-scroll">
{moments.map((moment, i) => (
<div key={i} className="h-full w-full snap-start relative bg-zinc-900">
<Image
src={`/.jpg?key=dsvkk&height=800&width=400&query=${moment.video || "esports_highlight"}`}
alt="Video"
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-black/60" />
{/* Right Sidebar Actions */}
<div className="absolute right-2 bottom-20 flex flex-col items-center gap-6">
<div className="flex flex-col items-center gap-1">
<div className="w-10 h-10 rounded-full border-2 border-white overflow-hidden relative">
<Image
src={`/.jpg?key=450fz&height=50&width=50&query=${moment.avatar || "avatar"}`}
alt="User"
fill
className="object-cover"
/>
</div>
<div className="w-4 h-4 bg-red-500 rounded-full flex items-center justify-center -mt-2 z-10">
<span className="text-[10px] font-bold text-white">+</span>
</div>
</div>
<div className="flex flex-col items-center gap-1">
<Heart size={28} className="text-white" />
<span className="text-xs font-bold">{moment.likes}</span>
</div>
<div className="flex flex-col items-center gap-1">
<MessageCircle size={28} className="text-white" />
<span className="text-xs font-bold">{moment.comments}</span>
</div>
<div className="flex flex-col items-center gap-1">
<Share2 size={28} className="text-white" />
<span className="text-xs font-bold"></span>
</div>
</div>
{/* Bottom Info */}
<div className="absolute left-4 bottom-20 w-3/4">
<h3 className="font-bold text-lg mb-2">@{moment.user}</h3>
<p className="text-sm opacity-90 leading-relaxed">{moment.desc}</p>
{moment.hasCourse && (
<div className="mt-3 inline-flex items-center gap-2 bg-white/10 backdrop-blur-md px-3 py-1.5 rounded-lg border border-white/10">
<Trophy size={14} className="text-yellow-400" />
<span className="text-xs font-bold text-yellow-100">: {moment.courseTitle}</span>
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
</div>
)

View File

@@ -1,6 +1,6 @@
"use client" // Convert to client component
import { Search, Bell, Flame, Trophy, Users, Star, Sword } from "lucide-react"
import { Search, Bell, Flame, Trophy, Users, Star, Sword, Crosshair, Crown } from "lucide-react"
import Image from "next/image"
import Link from "next/link"
import { toast } from "@/hooks/use-toast" // Import toast
@@ -95,27 +95,37 @@ export default function HomePage() {
</section>
{/* Game Icons */}
<div className="grid grid-cols-5 gap-3">
<div className="grid grid-cols-4 gap-3 px-2">
{[
{ name: "英雄联盟", icon: "LoL" },
{ name: "无畏契约", icon: "Val" },
{ name: "王者荣耀", icon: "HoK" },
{ name: "CS:GO", icon: "CS" },
{ name: "英雄联盟", icon: "Sword", color: "text-blue-400", bg: "bg-blue-500/10", link: "/games/lol" },
{ name: "无畏契约", icon: "Crosshair", color: "text-red-400", bg: "bg-red-500/10", link: "/games/val" },
{ name: "王者荣耀", icon: "Crown", color: "text-yellow-400", bg: "bg-yellow-500/10", link: "/games/hok" },
{ name: "CS:GO", icon: "Target", color: "text-orange-400", bg: "bg-orange-500/10", link: "/games/csgo" },
].map((game, i) => (
<div key={i} className="flex flex-col items-center gap-2 group cursor-pointer">
<div className="w-12 h-12 rounded-xl glass-card flex items-center justify-center border border-white/5 group-hover:border-primary/50 transition-colors relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<span className="text-xs font-bold text-white/80 group-hover:text-primary transition-colors">
{game.icon}
</span>
<Link
href={game.link}
key={i}
className="flex flex-col items-center gap-2 group cursor-pointer p-2 rounded-xl hover:bg-white/5 transition-colors"
>
<div
className={`w-12 h-12 rounded-2xl ${game.bg} flex items-center justify-center border border-white/5 group-hover:scale-105 transition-transform relative overflow-hidden`}
>
<div className={`absolute inset-0 bg-current opacity-10 ${game.color}`} />
{/* Using Lucide icons mapped manually for now as dynamic import is complex in this snippet */}
{game.icon === "Sword" && <Sword className={game.color} size={24} />}
{game.icon === "Crosshair" && <Crosshair className={game.color} size={24} />}
{game.icon === "Crown" && <Crown className={game.color} size={24} />}
{game.icon === "Target" && <Crosshair className={game.color} size={24} />}
</div>
<span className="text-[10px] text-white/60 group-hover:text-white transition-colors">{game.name}</span>
</div>
<span className="text-xs font-medium text-white/80 group-hover:text-white transition-colors">
{game.name}
</span>
</Link>
))}
</div>
{/* Industry Recommendations */}
<section className="space-y-3">
<section className="space-y-3 mt-2">
<div className="flex items-center justify-between">
<h3 className="font-bold text-lg flex items-center gap-2">
<Flame size={18} className="text-red-500" />
@@ -124,26 +134,44 @@ export default function HomePage() {
</div>
<div className="grid grid-cols-2 gap-3">
<div
className="glass-card p-3 rounded-xl border-l-2 border-purple-500/50 relative overflow-hidden group cursor-pointer"
onClick={() => toast({ title: "即将开放", description: "电竞酒店预订功能即将上线" })}
<Link
href="/hotel"
className="glass-card p-0 rounded-xl border-0 relative overflow-hidden group cursor-pointer aspect-[4/3]"
>
<div className="absolute right-0 top-0 p-2 opacity-10 group-hover:opacity-20 transition-opacity">
<Trophy size={40} />
<Image
src="/esports-hotel-cyberpunk-room.jpg"
alt="Hotel"
fill
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-transparent" />
<div className="absolute bottom-0 left-0 p-3 w-full">
<div className="flex items-center justify-between mb-1">
<h4 className="font-bold text-sm text-white"></h4>
<Trophy size={14} className="text-yellow-500" />
</div>
<p className="text-[10px] text-white/70"> </p>
</div>
<h4 className="font-bold text-sm text-purple-100"></h4>
<p className="text-[10px] text-white/60 mt-1"> </p>
</div>
<div
className="glass-card p-3 rounded-xl border-l-2 border-green-500/50 relative overflow-hidden group cursor-pointer"
onClick={() => toast({ title: "即将开放", description: "网咖预订功能即将上线" })}
</Link>
<Link
href="/hotel"
className="glass-card p-0 rounded-xl border-0 relative overflow-hidden group cursor-pointer aspect-[4/3]"
>
<div className="absolute right-0 top-0 p-2 opacity-10 group-hover:opacity-20 transition-opacity">
<Users size={40} />
<Image
src="/internet-cafe-luxury-interior.jpg"
alt="Cafe"
fill
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-transparent" />
<div className="absolute bottom-0 left-0 p-3 w-full">
<div className="flex items-center justify-between mb-1">
<h4 className="font-bold text-sm text-white"></h4>
<Users size={14} className="text-blue-500" />
</div>
<p className="text-[10px] text-white/70"> </p>
</div>
<h4 className="font-bold text-sm text-green-100"></h4>
<p className="text-[10px] text-white/60 mt-1"> </p>
</div>
</Link>
</div>
</section>
@@ -167,7 +195,7 @@ export default function HomePage() {
id: "50310",
members: 26,
tags: ["三角洲", "无畏契约"],
logo: "esports team logo blue",
logo: "esports_team_blue_logo",
},
{
name: "机枪电竞",
@@ -175,7 +203,7 @@ export default function HomePage() {
id: "50130",
members: 60,
tags: ["LOL", "云顶"],
logo: "esports team logo red",
logo: "esports_team_red_logo",
},
{
name: "东东电竞",
@@ -183,16 +211,17 @@ export default function HomePage() {
id: "88888",
members: 1205,
tags: ["全能", "陪玩"],
logo: "esports team logo gold",
logo: "esports_team_gold_logo",
},
].map((club, i) => (
<div
<Link
href={`/club/${club.id}`}
key={i}
className="glass-card p-3 rounded-xl flex items-center gap-3 hover:bg-white/5 transition-colors cursor-pointer"
>
<div className="w-14 h-14 rounded-lg bg-muted relative overflow-hidden shrink-0">
<div className="w-14 h-14 rounded-lg bg-muted relative overflow-hidden shrink-0 border border-white/10">
<Image
src={`/.png?height=100&width=100&query=${club.logo}`}
src={`/.jpg?key=6lzl1&height=100&width=100&query=${club.logo}`}
alt={club.name}
fill
className="object-cover"
@@ -222,7 +251,7 @@ export default function HomePage() {
</span>
</div>
</div>
</div>
</Link>
))}
</div>
</section>
@@ -291,7 +320,7 @@ export default function HomePage() {
>
<div className="relative aspect-[3/4]">
<Image
src={`/.jpg?height=400&width=300&query=${star.image}`}
src={`/.jpg?key=ao2up&height=400&width=300&query=${star.image}`}
alt={star.name}
fill
className="object-cover transition-transform duration-500 group-hover:scale-110"

173
app/party/[id]/page.tsx Normal file
View File

@@ -0,0 +1,173 @@
"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import Image from "next/image"
import { Mic, MicOff, MessageSquare, Gift, LogOut, MoreHorizontal, Send, UserPlus } from "lucide-react"
import { Button } from "@/components/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area"
import { toast } from "@/hooks/use-toast"
import { Input } from "@/components/ui/input"
export default function PartyRoomPage({ params }: { params: { id: string } }) {
const router = useRouter()
const [isMicOn, setIsMicOn] = useState(false)
const [messages, setMessages] = useState([
{ user: "系统", content: "欢迎来到 玩值电竞 派对房间", type: "system" },
{ user: "小柠檬", content: "大家好呀~", type: "user" },
{ user: "GM-远洋", content: "来个唱歌好听的", type: "user" },
])
const [inputValue, setInputValue] = useState("")
const seats = Array(8)
.fill(null)
.map((_, i) => ({
id: i,
user:
i < 3
? {
name: i === 0 ? "房主" : `用户${i}`,
avatar: i === 0 ? "gamer-girl-headphones.jpg" : "abstract-geometric-shapes.png",
isMuted: i === 2,
}
: null,
}))
const handleSendMessage = () => {
if (!inputValue.trim()) return
setMessages([...messages, { user: "我", content: inputValue, type: "user" }])
setInputValue("")
}
return (
<div className="min-h-screen bg-black relative flex flex-col">
{/* Background */}
<div className="absolute inset-0 z-0">
<Image
src="https://images.unsplash.com/photo-1516280440614-6697288d5d38?w=800&q=80"
alt="Background"
fill
className="object-cover opacity-30 blur-xl"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/50 via-black/20 to-black/90" />
</div>
{/* Header */}
<header className="relative z-10 px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-3">
<Button variant="ghost" size="icon" className="text-white" onClick={() => router.back()}>
<LogOut className="w-5 h-5" />
</Button>
<div>
<h1 className="text-white font-bold text-sm">耀</h1>
<p className="text-white/60 text-[10px]">ID: {params.id} | 在线: 125</p>
</div>
</div>
<Button variant="ghost" size="icon" className="text-white">
<MoreHorizontal className="w-5 h-5" />
</Button>
</header>
{/* Seats Grid */}
<div className="relative z-10 flex-1 px-4 py-6">
<div className="grid grid-cols-4 gap-4 gap-y-8">
{seats.map((seat) => (
<div key={seat.id} className="flex flex-col items-center gap-2">
<div className="relative w-14 h-14">
<div className="w-14 h-14 rounded-full bg-white/10 border border-white/20 flex items-center justify-center overflow-hidden relative">
{seat.user ? (
<Image
src={
seat.user.avatar === "gamer-girl-headphones.jpg"
? "/gamer-girl-headphones.jpg"
: `/abstract-geometric-shapes.png?height=100&width=100&query=avatar${seat.id}`
}
alt={seat.user.name}
fill
className="object-cover"
/>
) : (
<div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center">
<UserPlus className="w-4 h-4 text-white/30" />
</div>
)}
</div>
{seat.user?.isMuted && (
<div className="absolute bottom-0 right-0 bg-black/50 rounded-full p-0.5 border border-white/20">
<MicOff className="w-3 h-3 text-red-500" />
</div>
)}
{/* Wave Animation for talking (mock) */}
{seat.id === 0 && (
<div className="absolute -inset-1 border-2 border-primary/50 rounded-full animate-pulse" />
)}
</div>
<span className="text-xs text-white/80 truncate max-w-[4rem]">
{seat.user ? seat.user.name : `${seat.id + 1}号麦`}
</span>
</div>
))}
</div>
</div>
{/* Chat Area */}
<div className="relative z-10 h-48 px-4 mb-2">
<ScrollArea className="h-full w-3/4 mask-image-linear-gradient-to-t">
<div className="space-y-2 flex flex-col justify-end min-h-full pb-2">
{messages.map((msg, i) => (
<div
key={i}
className={`bg-black/40 backdrop-blur-sm px-3 py-1.5 rounded-xl self-start max-w-full ${msg.type === "system" ? "text-yellow-400" : "text-white"}`}
>
<span className="text-white/60 text-xs mr-2">{msg.user}:</span>
<span className="text-sm">{msg.content}</span>
</div>
))}
</div>
</ScrollArea>
</div>
{/* Bottom Controls */}
<div className="relative z-10 bg-black/80 backdrop-blur-md px-4 py-3 border-t border-white/10 pb-8">
<div className="flex items-center gap-3 mb-3">
<Input
className="bg-white/10 border-none text-white h-9"
placeholder="聊点什么..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
/>
<Button size="icon" className="bg-primary hover:bg-primary/90 h-9 w-9" onClick={handleSendMessage}>
<Send className="w-4 h-4" />
</Button>
</div>
<div className="flex items-center justify-between px-2">
<div className="flex gap-6">
<div className="flex flex-col items-center gap-1 cursor-pointer" onClick={() => setIsMicOn(!isMicOn)}>
<div
className={`w-10 h-10 rounded-full flex items-center justify-center ${isMicOn ? "bg-white text-black" : "bg-white/10 text-white"}`}
>
{isMicOn ? <Mic className="w-5 h-5" /> : <MicOff className="w-5 h-5" />}
</div>
<span className="text-[10px] text-white/60">{isMicOn ? "开麦" : "闭麦"}</span>
</div>
<div className="flex flex-col items-center gap-1 cursor-pointer">
<div className="w-10 h-10 rounded-full bg-white/10 text-white flex items-center justify-center">
<MessageSquare className="w-5 h-5" />
</div>
<span className="text-[10px] text-white/60"></span>
</div>
</div>
<Button
className="rounded-full px-6 bg-gradient-to-r from-pink-500 to-purple-500 border-none shadow-lg shadow-pink-500/20"
onClick={() => toast({ title: "礼物发送", description: "送出一个 🚀 火箭" })}
>
<Gift className="w-5 h-5 mr-2" />
</Button>
</div>
</div>
</div>
)
}

View File

@@ -3,23 +3,74 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Mic, Users, Gamepad2, Headphones, Music, Heart, Sparkles, Filter } from "lucide-react"
import { Mic, Users, Gamepad2, Headphones, Heart, Sparkles, Filter, Trophy, Crosshair } from "lucide-react"
import Image from "next/image"
import { useState } from "react"
import { toast } from "@/hooks/use-toast"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { useRouter } from "next/navigation"
type MatchCategory = "star" | "guild" | "cp" | "coach"
export default function PlanetPage() {
const router = useRouter()
const [isMatching, setIsMatching] = useState(false)
const [matchResult, setMatchResult] = useState<any>(null)
const [selectedCategory, setSelectedCategory] = useState<MatchCategory>("star") // Default to "star" (电竞明星)
const handleMatch = () => {
setIsMatching(true)
setMatchResult(null)
setTimeout(() => {
setIsMatching(false)
const matchResults = {
star: {
name: "GM-远洋",
title: "国服第一盲僧 · 电竞明星",
avatar: "/esports-pro-gamer-male-jersey-confident.jpg",
viewers: "12.5w",
tags: ["LOL", "技术流"],
roomId: 1001,
},
guild: {
name: "东东电竞公会",
title: "顶级主播公会 · 月入50万+",
avatar: "/esports-guild-logo.jpg",
viewers: "1205",
tags: ["全能", "培训"],
roomId: 1002,
},
cp: {
name: "甜心辅助",
title: "温柔妹子 · 找电竞CP",
avatar: "/cute-gamer-girl.jpg",
viewers: "8.3w",
tags: ["王者", "温柔"],
roomId: 1003,
},
coach: {
name: "职业陪练-小明",
title: "前职业选手 · 游戏陪练",
avatar: "/game-coach-avatar.jpg",
viewers: "3.2w",
tags: ["陪练", "上分"],
roomId: 1004,
},
}
const result = matchResults[selectedCategory]
setMatchResult(result)
toast({
title: "匹配成功",
description: "已为您找到一位灵魂伴侣",
description: `为您找到:${result.name}`,
})
setTimeout(() => {
router.push(`/chat/${result.roomId}`)
}, 1500)
}, 3000)
}
@@ -28,6 +79,19 @@ export default function PlanetPage() {
title: "正在加入派对",
description: `即将进入房间:${title}`,
})
setTimeout(() => {
router.push(`/party/${Math.floor(Math.random() * 10000)}`)
}, 1000)
}
const handleJoinTeam = (title: string) => {
toast({
title: "正在加入队伍",
description: `即将进入房间:${title}`,
})
setTimeout(() => {
router.push(`/team/${Math.floor(Math.random() * 10000)}`)
}, 1000)
}
return (
@@ -80,7 +144,6 @@ export default function PlanetPage() {
<Button className="w-full bg-primary hover:bg-primary/90"></Button>
</DialogContent>
</Dialog>
{/* </CHANGE> */}
<Button variant="ghost" size="icon" className="rounded-full bg-white/5">
<Users className="w-5 h-5" />
</Button>
@@ -112,12 +175,16 @@ export default function PlanetPage() {
<TabsContent value="voice" className="mt-0">
<div className="flex flex-col items-center justify-center py-12 space-y-8">
<div className="relative w-64 h-64">
<div
className={`absolute inset-0 bg-primary/20 rounded-full ${isMatching ? "animate-ping duration-1000" : "animate-ping duration-3000"}`}
/>
<div
className={`absolute inset-4 bg-primary/30 rounded-full ${isMatching ? "animate-pulse duration-500" : "animate-pulse duration-2000"}`}
/>
{(!matchResult || isMatching) && (
<>
<div
className={`absolute inset-0 bg-primary/20 rounded-full ${isMatching ? "animate-ping duration-1000" : "animate-ping duration-3000"}`}
/>
<div
className={`absolute inset-4 bg-primary/30 rounded-full ${isMatching ? "animate-pulse duration-500" : "animate-pulse duration-2000"}`}
/>
</>
)}
{isMatching && (
<>
@@ -126,45 +193,74 @@ export default function PlanetPage() {
</>
)}
<div className="absolute inset-0 flex items-center justify-center">
<div
className="w-48 h-48 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center shadow-2xl shadow-primary/50 border-4 border-white/10 relative overflow-hidden group cursor-pointer transition-transform hover:scale-105 active:scale-95"
onClick={handleMatch}
>
<div className="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=800&q=80')] opacity-20 bg-cover mix-blend-overlay" />
<div className="text-center z-10">
{isMatching ? (
<>
<Sparkles className="w-12 h-12 text-white mx-auto mb-2 animate-spin" />
<span className="text-xl font-bold text-white">...</span>
</>
) : (
<>
<Mic className="w-12 h-12 text-white mx-auto mb-2" />
<span className="text-xl font-bold text-white"></span>
<p className="text-xs text-white/70 mt-1"></p>
</>
)}
{matchResult ? (
<div className="absolute inset-0 flex items-center justify-center z-20">
<div className="w-64 h-auto bg-zinc-900 border border-primary/50 rounded-2xl p-5 flex flex-col items-center text-center shadow-2xl shadow-primary/30 animate-in zoom-in duration-300">
<div className="w-20 h-20 rounded-full border-2 border-primary p-1 mb-3 relative">
<Image
src={`/.jpg?key=cc76z&height=100&width=100&query=${matchResult.avatar}`}
alt="Streamer"
fill
className="rounded-full object-cover"
/>
<div className="absolute -bottom-1 bg-primary text-[10px] px-2 py-0.5 rounded-full text-white font-bold border-2 border-zinc-900">
MATCHED
</div>
</div>
<h3 className="font-bold text-lg text-white mb-0.5">{matchResult.name}</h3>
<p className="text-xs text-white/50 mb-4">{matchResult.title}</p>
<div className="w-full bg-white/5 rounded-lg p-3 mb-4 text-left">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
<span className="text-xs font-bold text-white/80">...</span>
</div>
<p className="text-[10px] text-white/40 line-clamp-2">
"兄弟们,这波团战怎么说?来个辅助跟我一起..."
</p>
</div>
<div className="w-full bg-green-500/10 border border-green-500/30 rounded-lg p-3 flex items-center justify-center gap-2">
<div className="w-2 h-2 rounded-full bg-green-500 animate-ping" />
<span className="text-xs font-bold text-green-400">...</span>
</div>
</div>
</div>
</div>
) : (
<div className="absolute inset-0 flex items-center justify-center">
<div
className="w-48 h-48 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center shadow-2xl shadow-primary/50 border-4 border-white/10 relative overflow-hidden group cursor-pointer transition-transform hover:scale-105 active:scale-95"
onClick={handleMatch}
>
<div className="absolute inset-0 bg-[url('/abstract-energy.jpg')] opacity-20 bg-cover mix-blend-overlay" />
<div className="text-center z-10">
{isMatching ? (
<>
<Sparkles className="w-12 h-12 text-white mx-auto mb-2 animate-spin" />
<span className="text-xl font-bold text-white">...</span>
</>
) : (
<>
<Mic className="w-12 h-12 text-white mx-auto mb-2" />
<span className="text-xl font-bold text-white"></span>
<p className="text-xs text-white/70 mt-1">
{selectedCategory === "star" && "寻找电竞明星"}
{selectedCategory === "guild" && "寻找主播公会"}
{selectedCategory === "cp" && "寻找电竞CP"}
{selectedCategory === "coach" && "寻找游戏陪练"}
</p>
</>
)}
</div>
</div>
</div>
)}
{/* Floating Avatars - Hide avatars when matching to reduce clutter */}
{!isMatching && (
{!isMatching && !matchResult && (
<>
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-4 animate-bounce duration-3000">
<div className="w-10 h-10 rounded-full border-2 border-primary bg-black overflow-hidden">
<Image src="/diverse-group-avatars.png" alt="User" width={40} height={40} />
</div>
</div>
<div className="absolute bottom-4 right-4 animate-bounce duration-4000 delay-500">
<div className="w-8 h-8 rounded-full border-2 border-purple-500 bg-black overflow-hidden">
<Image src="/gamer-girl-headphones.jpg" alt="User" width={32} height={32} />
</div>
</div>
<div className="absolute bottom-8 left-4 animate-bounce duration-5000 delay-1000">
<div className="w-12 h-12 rounded-full border-2 border-pink-500 bg-black overflow-hidden">
<Image src="/gamer-boy-esports-jersey.jpg" alt="User" width={48} height={48} />
<Image src="/gamer-avatar.jpg" alt="User" width={40} height={40} />
</div>
</div>
</>
@@ -174,24 +270,27 @@ export default function PlanetPage() {
<div className="grid grid-cols-3 gap-4 w-full">
<Button
variant="outline"
className="flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50"
onClick={() => setSelectedCategory("guild")}
className={`flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50 ${selectedCategory === "guild" ? "border-blue-500 bg-blue-500/10" : ""}`}
>
<Users className="w-6 h-6 text-blue-500 mb-2" />
<span className="text-xs"></span>
</Button>
<Button
variant="outline"
onClick={() => setSelectedCategory("cp")}
className={`flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50 ${selectedCategory === "cp" ? "border-pink-500 bg-pink-500/10" : ""}`}
>
<Heart className="w-6 h-6 text-pink-500 mb-2" />
<span className="text-xs"></span>
<span className="text-xs">CP</span>
</Button>
<Button
variant="outline"
className="flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50"
>
<Music className="w-6 h-6 text-blue-500 mb-2" />
<span className="text-xs"></span>
</Button>
<Button
variant="outline"
className="flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50"
onClick={() => setSelectedCategory("coach")}
className={`flex flex-col h-auto py-4 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50 ${selectedCategory === "coach" ? "border-green-500 bg-green-500/10" : ""}`}
>
<Gamepad2 className="w-6 h-6 text-green-500 mb-2" />
<span className="text-xs"></span>
<span className="text-xs"></span>
</Button>
</div>
</div>
@@ -200,46 +299,42 @@ export default function PlanetPage() {
<TabsContent value="party" className="space-y-4">
{[
{
title: "王者荣耀五排车队",
title: "GM-远洋的粉丝群",
tag: "直播中",
count: 1205,
image: "/esports-tournament-stadium.jpg",
isLive: true,
streamer: "GM-远洋",
},
{
title: "带粉上分车队",
tag: "游戏",
count: 125,
image: "https://images.unsplash.com/photo-1542751371-adc38448a05e?w=800&q=80",
},
{
title: "深夜电台 治愈系",
tag: "聊天",
count: 892,
image: "https://images.unsplash.com/photo-1478737270239-2f02b77ac6d5?w=800&q=80",
},
{
title: "KTV 麦霸进",
tag: "K歌",
count: 45,
image: "https://images.unsplash.com/photo-1516280440614-6697288d5d38?w=800&q=80",
},
{
title: "和平精英 刚枪",
tag: "游戏",
count: 230,
image: "https://images.unsplash.com/photo-1538481199705-c710c4e965fc?w=800&q=80",
count: 89,
image: "/gaming-room-rgb.jpg",
isLive: false,
streamer: "路人王",
},
].map((room, i) => (
<Card
key={i}
className="bg-white/5 border-white/10 overflow-hidden hover:bg-white/10 transition-colors cursor-pointer"
onClick={() => handleJoinParty(room.title)} // Added click handler
className="bg-white/5 border-white/10 overflow-hidden hover:bg-white/10 transition-colors cursor-pointer relative"
onClick={() => router.push(`/chat/${1000 + i}`)}
>
<div className="flex p-3 gap-4">
<div className="relative w-20 h-20 rounded-lg overflow-hidden flex-shrink-0">
<div className="relative w-20 h-20 rounded-lg overflow-hidden flex-shrink-0 border border-white/10">
<Image src={room.image || "/placeholder.svg"} alt={room.title} fill className="object-cover" />
<div className="absolute top-1 left-1 bg-black/60 text-[10px] px-1.5 py-0.5 rounded text-white backdrop-blur-sm">
{room.tag}
</div>
{room.isLive && (
<div className="absolute top-1 left-1 bg-primary/90 text-[8px] px-1.5 py-0.5 rounded text-white backdrop-blur-sm flex items-center gap-1 animate-pulse">
<span className="w-1 h-1 rounded-full bg-white" /> LIVE
</div>
)}
</div>
<div className="flex-1 flex flex-col justify-between py-1">
<div>
<h3 className="font-bold text-white line-clamp-1">{room.title}</h3>
<p className="text-xs text-white/60 mt-1 line-clamp-2"></p>
<p className="text-xs text-white/60 mt-1 line-clamp-2">
{room.isLive ? `主播 ${room.streamer} 正在直播,点击围观!` : "进群一起开黑,缺人中..."}
</p>
</div>
<div className="flex items-center justify-between mt-2">
<div className="flex -space-x-2">
@@ -249,7 +344,7 @@ export default function PlanetPage() {
className="w-6 h-6 rounded-full border border-black bg-gray-800 overflow-hidden"
>
<Image
src={`/abstract-geometric-shapes.png?height=24&width=24&query=user${avatar}`}
src={`/gamer-avatar.jpg?key=mup1i&height=24&width=24&query=gamer avatar ${avatar}`}
alt="User"
width={24}
height={24}
@@ -264,15 +359,76 @@ export default function PlanetPage() {
</div>
</div>
</div>
{room.isLive && (
<div className="absolute right-3 bottom-3">
<Button
size="sm"
className="h-6 text-[10px] px-2 bg-primary/20 text-primary hover:bg-primary hover:text-white border border-primary/20"
>
</Button>
</div>
)}
</Card>
))}
</TabsContent>
<TabsContent value="team" className="space-y-4">
<div className="text-center py-12 text-white/50">
<Gamepad2 className="w-12 h-12 mx-auto mb-4 opacity-50" />
<p>...</p>
<div className="rounded-xl bg-gradient-to-r from-purple-900/50 to-blue-900/50 p-4 border border-white/10 relative overflow-hidden mb-4">
<div className="relative z-10">
<div className="flex items-center gap-2 mb-2">
<Trophy className="text-yellow-400" size={20} />
<h3 className="font-bold text-lg italic">2025 </h3>
</div>
<p className="text-xs text-white/70 mb-3"></p>
<Button size="sm" className="bg-yellow-500 text-black hover:bg-yellow-400 font-bold text-xs">
</Button>
</div>
<Image
src="/esports-trophy.png"
alt="Trophy"
width={100}
height={100}
className="absolute right-0 bottom-0 opacity-20"
/>
</div>
<h3 className="font-bold text-sm mb-2 flex items-center gap-2">
<Crosshair size={16} className="text-primary" />
</h3>
{[
{ game: "LOL", mode: "灵活组排", rank: "钻石", waiting: "3/5", title: "钻石冲大师,来听指挥的" },
{ game: "无畏契约", mode: "竞技模式", rank: "超凡", waiting: "1/5", title: "缺个烟位,速来" },
{ game: "王者荣耀", mode: "五排", rank: "王者", waiting: "4/5", title: "缺辅助,来个软辅妹妹" },
].map((team, i) => (
<div key={i} className="glass-card p-3 rounded-xl flex justify-between items-center">
<div>
<div className="flex items-center gap-2 mb-1">
<span className="text-xs font-bold bg-white/10 px-1.5 py-0.5 rounded text-white/80">{team.game}</span>
<span className="text-sm font-bold">{team.title}</span>
</div>
<div className="flex items-center gap-3 text-xs text-white/50">
<span>{team.mode}</span>
<span>{team.rank}</span>
</div>
</div>
<div className="flex flex-col items-end gap-1">
<span className="text-xs font-bold text-primary">{team.waiting}</span>
<Button
size="sm"
className="h-7 text-xs"
onClick={() => {
toast({ title: "加入成功", description: `已加入:${team.title}` })
setTimeout(() => router.push(`/chat/${2000 + i}`), 1000)
}}
>
</Button>
</div>
</div>
))}
</TabsContent>
</Tabs>
</div>

View File

@@ -1,31 +1,31 @@
"use client" // Convert to client component
"use client"
import {
Settings,
ChevronRight,
LayoutGrid,
FileText,
ShoppingBag,
MessageSquare,
Flag,
Eye,
Footprints,
Presentation,
Star,
Crown,
BookOpen,
Diamond,
HeartIcon,
Bell,
LogOut,
Wallet,
Ticket,
History,
User,
Medal,
} from "lucide-react"
import Image from "next/image"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { useAppContext } from "@/components/providers/app-provider" // Import context
import { toast } from "@/hooks/use-toast" // Import toast
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useAppContext } from "@/components/providers/app-provider"
import { toast } from "@/hooks/use-toast"
import { Card, CardContent } from "@/components/ui/card"
import { useRouter } from "next/navigation"
export default function ProfilePage() {
const { user, wallet, recharge } = useAppContext() // Use context
const { user, wallet, recharge } = useAppContext()
const router = useRouter()
const handleComingSoon = (feature: string) => {
toast({
@@ -35,236 +35,176 @@ export default function ProfilePage() {
}
return (
<div className="min-h-screen pb-24 bg-background">
{/* Header */}
<div className="relative h-48 bg-gradient-to-b from-primary/10 to-background">
<div className="absolute top-4 right-4 flex gap-4">
<Link href="/pitch-deck">
<Presentation className="text-white/80 hover:text-primary transition-colors" />
</Link>
<button onClick={() => handleComingSoon("设置")}>
<Settings className="text-white/80 hover:text-primary transition-colors" />
</button>
</div>
<div className="min-h-screen pb-24 bg-black">
{/* Header Area */}
<div className="relative h-64 bg-gradient-to-b from-zinc-800 to-black">
<div className="absolute top-0 inset-x-0 h-full bg-[url('/abstract-esports-pattern.jpg')] opacity-20 bg-cover bg-center mix-blend-overlay" />
<div className="absolute -bottom-10 left-4 flex items-end gap-4">
<div className="w-20 h-20 rounded-full border-4 border-background bg-muted relative overflow-hidden">
<Image src={user.avatar || "/placeholder.svg"} alt="Profile" fill className="object-cover" />
</div>
<div className="mb-2">
<h1 className="text-xl font-bold flex items-center gap-2">
{user.name}
<span className="px-1.5 py-0.5 rounded bg-primary text-[10px] text-white font-bold">LV.{user.level}</span>
</h1>
<p className="text-xs text-white/50">ID: {user.id || "888888"}</p>
</div>
<button
className="mb-2 px-3 py-1 rounded-full bg-white/10 text-xs font-medium ml-auto flex items-center gap-1 hover:bg-white/20 transition-colors"
onClick={() => handleComingSoon("个人主页")}
>
<ChevronRight size={12} />
</button>
</div>
</div>
{/* Stats */}
<div className="mt-12 px-6 flex justify-between text-center">
{[
{ label: "动态", value: user.activity || 0 },
{ label: "关注", value: user.following || 0 },
{ label: "粉丝", value: user.followers || 0 },
{ label: "访客", value: user.visitors || 0 },
].map((stat, i) => (
<div key={i} className="flex flex-col gap-1">
<span className="font-bold text-lg">{stat.value}</span>
<span className="text-xs text-white/50">{stat.label}</span>
</div>
))}
</div>
{/* VIP Banner */}
<div
className="mx-4 mt-6 p-3 rounded-xl bg-gradient-to-r from-yellow-600/20 to-yellow-900/20 border border-yellow-500/20 flex items-center justify-between cursor-pointer hover:bg-yellow-600/30 transition-colors"
onClick={() => handleComingSoon("会员中心")}
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-yellow-500/20 flex items-center justify-center">
<Crown size={16} className="text-yellow-500" />
</div>
<div>
<h3 className="text-sm font-bold text-yellow-500"></h3>
<p className="text-[10px] text-yellow-500/60">12</p>
</div>
</div>
<Button
size="sm"
variant="outline"
className="h-7 text-xs border-yellow-500/30 text-yellow-500 hover:bg-yellow-500/10 bg-transparent"
>
</Button>
</div>
{/* Wallet Card */}
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-r from-[#1a1a24] to-[#0f0f16] border border-white/5 relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/5 rounded-full blur-3xl" />
<div className="flex items-center justify-between mb-4">
<span className="text-sm font-medium text-white/80"></span>
<Button
size="sm"
className="h-6 text-xs bg-primary/20 hover:bg-primary/30 text-primary border-none"
onClick={() => recharge(100)} // Add recharge handler
>
</Button>
</div>
<div className="flex justify-between items-center">
<div>
<div className="flex items-center gap-1 text-primary mb-1">
<Diamond size={16} />
<span className="text-xs"></span>
<div className="relative z-10 pt-safe px-4">
<div className="flex justify-between items-center h-12 mb-4">
<Button
variant="ghost"
size="icon"
className="text-white/80 hover:text-white hover:bg-white/10"
onClick={() => handleComingSoon("设置")}
>
<Settings size={20} />
</Button>
<div className="flex gap-2">
<Button
variant="ghost"
size="icon"
className="text-white/80 hover:text-white hover:bg-white/10"
onClick={() => handleComingSoon("消息")}
>
<Bell size={20} />
</Button>
</div>
<span className="text-2xl font-bold font-mono">{wallet.diamonds}</span> {/* Use wallet data */}
</div>
<div className="w-px h-8 bg-white/10" />
<div>
<div className="flex items-center gap-1 text-secondary mb-1">
<div className="w-4 h-4 rounded-full bg-secondary/20 flex items-center justify-center text-[10px]">
🐱
<div className="flex items-center gap-4 mb-6">
<div className="relative w-20 h-20 rounded-full border-2 border-primary p-1">
<Image
src={user.avatar || "/placeholder.svg?height=100&width=100&query=gamer avatar"}
alt="Profile"
fill
className="rounded-full object-cover"
/>
<div className="absolute bottom-0 right-0 bg-primary text-[10px] px-1.5 py-0.5 rounded-full text-white font-bold border border-black">
LV.8
</div>
<span className="text-xs"></span> {/* Updated label to Wanzi Coin */}
</div>
<span className="text-2xl font-bold font-mono">{wallet.coins}</span>
<div>
<h2 className="text-xl font-bold text-white mb-1">{user.name}</h2>
<p className="text-xs text-white/60 bg-white/10 px-2 py-0.5 rounded-full inline-block">
ID: {user.id || "888888"}
</p>
</div>
</div>
<div className="flex justify-between text-white/90 px-4">
<div className="text-center">
<div className="text-lg font-bold">{user.activity || 0}</div>
<div className="text-xs text-white/50"></div>
</div>
<div className="text-center">
<div className="text-lg font-bold">{user.following || 0}</div>
<div className="text-xs text-white/50"></div>
</div>
<div className="text-center">
<div className="text-lg font-bold">{user.followers || 0}</div>
<div className="text-xs text-white/50"></div>
</div>
</div>
</div>
</div>
{/* Creator Center */}
<div className="mx-4 mt-4 glass-card rounded-2xl p-4">
<h3 className="text-sm font-bold mb-4 flex items-center gap-2">
<Star size={16} className="text-yellow-400" />
</h3>
<div className="grid grid-cols-4 gap-4">
<Link
href="/creator/apply"
className="col-span-2 bg-gradient-to-br from-primary/20 to-purple-500/20 rounded-xl p-3 border border-white/5 relative overflow-hidden cursor-pointer hover:opacity-80 transition-opacity"
>
<div className="relative z-10">
<h4 className="font-bold text-sm"></h4>
<p className="text-[10px] text-white/60 mt-1"> </p>
</div>
<Star className="absolute bottom-0 right-0 text-primary/20 w-12 h-12 -mb-2 -mr-2" />
</Link>
<Link
href="/guild"
className="col-span-2 bg-gradient-to-br from-blue-500/20 to-cyan-500/20 rounded-xl p-3 border border-white/5 relative overflow-hidden cursor-pointer hover:opacity-80 transition-opacity"
>
<div className="relative z-10">
<h4 className="font-bold text-sm"></h4>
<p className="text-[10px] text-white/60 mt-1"> </p>
</div>
<Flag className="absolute bottom-0 right-0 text-blue-500/20 w-12 h-12 -mb-2 -mr-2" />
</Link>
{/* Content Area */}
<div className="px-4 -mt-6 relative z-20 space-y-4">
{/* VIP Card */}
<div
className="bg-gradient-to-r from-yellow-600 to-yellow-800 rounded-xl p-4 text-white shadow-lg shadow-yellow-900/20"
onClick={() => handleComingSoon("会员中心")}
>
<div className="flex justify-between items-center mb-2">
<h3 className="font-bold flex items-center gap-2">
<Medal size={16} className="text-yellow-200" />
</h3>
<span className="text-xs bg-black/20 px-2 py-1 rounded-full"></span>
</div>
<p className="text-xs text-yellow-100/80">89</p>
</div>
</div>
{/* Moments Tabs Section */}
<div className="mx-4 mt-6">
<Tabs defaultValue="moments" className="w-full">
<TabsList className="w-full bg-transparent border-b border-white/10 rounded-none p-0 h-auto justify-start gap-6">
<TabsTrigger
value="moments"
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-white px-0 pb-2 text-white/60"
<Card className="bg-zinc-900/50 border-white/5">
<CardContent className="p-4 grid grid-cols-4 gap-4">
<Link
href="/mall?tab=wallet"
className="flex flex-col items-center gap-2 cursor-pointer active:scale-95 transition-transform"
>
</TabsTrigger>
<TabsTrigger
value="likes"
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-white px-0 pb-2 text-white/60"
<div className="w-10 h-10 rounded-full bg-blue-500/10 flex items-center justify-center text-blue-500">
<Wallet size={20} />
</div>
<span className="text-xs text-white/70"></span>
</Link>
<Link
href="/coupons"
className="flex flex-col items-center gap-2 cursor-pointer active:scale-95 transition-transform"
>
</TabsTrigger>
</TabsList>
<div className="w-10 h-10 rounded-full bg-purple-500/10 flex items-center justify-center text-purple-500">
<Ticket size={20} />
</div>
<span className="text-xs text-white/70"></span>
</Link>
<Link
href="/footprint"
className="flex flex-col items-center gap-2 cursor-pointer active:scale-95 transition-transform"
>
<div className="w-10 h-10 rounded-full bg-green-500/10 flex items-center justify-center text-green-500">
<History size={20} />
</div>
<span className="text-xs text-white/70"></span>
</Link>
<Link
href="/dress-up"
className="flex flex-col items-center gap-2 cursor-pointer active:scale-95 transition-transform"
>
<div className="w-10 h-10 rounded-full bg-pink-500/10 flex items-center justify-center text-pink-500">
<User size={20} />
</div>
<span className="text-xs text-white/70"></span>
</Link>
</CardContent>
</Card>
<TabsContent value="moments" className="mt-4">
<div className="grid grid-cols-3 gap-1">
{[1, 2, 3, 4, 5].map((i) => (
<div key={i} className="aspect-[3/4] bg-white/5 relative group cursor-pointer">
<Image
src={`/esports-gamer-girl-streaming-vertical.jpg?height=200&width=150&query=moment${i}`}
alt="Moment"
fill
className="object-cover opacity-80 group-hover:opacity-100 transition-opacity"
/>
<div className="absolute bottom-1 left-1 flex items-center gap-1 text-[10px] text-white drop-shadow-md">
<HeartIcon size={10} className="fill-white" />
<span>{100 * i + 25}</span>
</div>
</div>
))}
</div>
</TabsContent>
<TabsContent value="likes" className="mt-4">
<div className="text-center py-12 text-white/30 text-sm">
<HeartIcon className="w-12 h-12 mx-auto mb-2 opacity-20" />
</div>
</TabsContent>
</Tabs>
</div>
{/* Menu Grid */}
<div className="mx-4 mt-4 glass-card rounded-2xl p-4">
<h3 className="text-sm font-bold mb-4 flex items-center gap-2">
<LayoutGrid size={16} className="text-accent" />
</h3>
<div className="grid grid-cols-4 gap-y-6">
{/* ... existing menu items ... */}
<div className="space-y-1 rounded-xl overflow-hidden bg-zinc-900/50 border border-white/5">
{[
{ icon: FileText, label: "订单中心", color: "text-green-400", href: "/orders" },
{ icon: BookOpen, label: "我的课程", color: "text-red-400", href: "/my-courses" }, // Changed to my-courses
{ icon: Presentation, label: "商业计划", color: "text-orange-400", href: "/pitch-deck" },
{
icon: MessageSquare,
label: "联系客服",
color: "text-purple-400",
action: () => handleComingSoon("客服系统"),
},
{ icon: ShoppingBag, label: "背包", color: "text-blue-400", action: () => handleComingSoon("背包系统") },
{ icon: Footprints, label: "足迹", color: "text-teal-400", action: () => handleComingSoon("足迹功能") },
{ icon: Eye, label: "访客", color: "text-yellow-400", action: () => handleComingSoon("访客记录") },
{ icon: Settings, label: "设置", color: "text-pink-400", action: () => handleComingSoon("设置") },
{ icon: FileText, label: "我的订单", href: "/orders" },
{ icon: BookOpen, label: "我的课程", href: "/my-courses" },
{ icon: Presentation, label: "商业计划", href: "/pitch-deck", hasBadge: true },
{ icon: MessageSquare, label: "联系客服", action: () => handleComingSoon("客服") },
].map((item, i) =>
item.href ? (
<Link
key={i}
href={item.href}
className="flex flex-col items-center gap-2 hover:opacity-80 transition-opacity"
className="w-full flex items-center justify-between p-4 hover:bg-white/5 active:bg-white/10 transition-colors border-b border-white/5 last:border-0"
>
<div className={`w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center ${item.color}`}>
<item.icon size={20} />
<span className="text-sm text-white flex items-center gap-3">
<item.icon size={18} className="text-white/60" />
{item.label}
</span>
<div className="flex items-center gap-2">
{item.hasBadge && (
<span className="text-[10px] bg-red-500 text-white px-1.5 py-0.5 rounded-full">New</span>
)}
<ChevronRight size={16} className="text-white/30" />
</div>
<span className="text-[10px] text-white/60">{item.label}</span>
</Link>
) : (
<button
key={i}
onClick={item.action}
className="flex flex-col items-center gap-2 hover:opacity-80 transition-opacity"
className="w-full flex items-center justify-between p-4 hover:bg-white/5 active:bg-white/10 transition-colors border-b border-white/5 last:border-0"
>
<div className={`w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center ${item.color}`}>
<item.icon size={20} />
</div>
<span className="text-[10px] text-white/60">{item.label}</span>
<span className="text-sm text-white flex items-center gap-3">
<item.icon size={18} className="text-white/60" />
{item.label}
</span>
<ChevronRight size={16} className="text-white/30" />
</button>
),
)}
</div>
<Button
variant="outline"
className="w-full border-white/10 text-white/50 hover:text-white hover:bg-white/10 hover:border-white/20 bg-transparent"
onClick={() => handleComingSoon("退出登录")}
>
<LogOut size={16} className="mr-2" /> 退
</Button>
</div>
</div>
)

View File

@@ -1,6 +1,6 @@
import { ArrowLeft, Star, Trophy, Users, Clock, Shield, Heart, MessageCircle, Share2, Sparkles } from 'lucide-react';
import Image from "next/image";
import Link from "next/link";
import { ArrowLeft, Star, Trophy, Users, Clock, Shield, Heart, MessageCircle, Share2, Sparkles } from "lucide-react"
import Image from "next/image"
import Link from "next/link"
// Mock data - in production this would come from a database
const starData: Record<string, any> = {
@@ -8,7 +8,7 @@ const starData: Record<string, any> = {
name: "GM-远洋",
title: "国服第一盲僧",
avatar: "/gamer-girl-headphones.jpg",
cover: "/esports-tournament-stadium-neon-lights-crowd.jpg",
cover: "/esports-tournament.png",
rating: 4.9,
orders: 1205,
fans: 46000,
@@ -28,7 +28,7 @@ const starData: Record<string, any> = {
price: 50,
duration: "1局",
desc: "一起开黑,边玩边教,轻松上分",
popular: true
popular: true,
},
{
id: "2",
@@ -37,7 +37,7 @@ const starData: Record<string, any> = {
price: 220,
duration: "5局",
desc: "连续5局陪玩,享受9折优惠",
discount: "9折"
discount: "9折",
},
{
id: "3",
@@ -54,31 +54,31 @@ const starData: Record<string, any> = {
price: 1980,
duration: "30天",
desc: "一对一长期指导,包含10次实战陪练+课程",
hot: true
}
hot: true,
},
],
reviews: [
{
user: "玩家***23",
avatar: "/diverse-avatars.png",
avatar: "/placeholder.svg?height=32&width=32",
rating: 5,
content: "远洋老师教的很好,盲僧R闪学会了!",
time: "2天前"
time: "2天前",
},
{
user: "电竞***爱好者",
avatar: "/diverse-avatars.png",
avatar: "/placeholder.svg?height=32&width=32",
rating: 5,
content: "非常耐心,讲解很详细,值得!",
time: "5天前"
}
]
time: "5天前",
},
],
},
"2": {
name: "卡若",
title: "职业战队退役选手",
avatar: "/gamer-boy-esports-jersey.jpg",
cover: "/esports-tournament-stadium-neon-lights-crowd.jpg",
avatar: "/placeholder.svg?height=200&width=200",
cover: "/placeholder.svg?height=400&width=600",
rating: 4.8,
orders: 856,
fans: 32000,
@@ -98,7 +98,7 @@ const starData: Record<string, any> = {
price: 80,
duration: "1局",
desc: "一起开黑,边玩边教,轻松上分",
popular: true
popular: true,
},
{
id: "2",
@@ -115,38 +115,36 @@ const starData: Record<string, any> = {
price: 2980,
duration: "30天",
desc: "一对一长期指导,包含15次实战陪练+课程",
hot: true
}
hot: true,
},
],
reviews: [
{
user: "召唤师***88",
avatar: "/diverse-avatars.png",
avatar: "/placeholder.svg?height=32&width=32",
rating: 5,
content: "卡若老师很专业,从白银打到钻石了!",
time: "1天前"
}
]
}
};
time: "1天前",
},
],
},
}
export default function StarDetailPage({ params }: { params: { id: string } }) {
const star = starData[params.id] || starData["1"];
const star = starData[params.id] || starData["1"]
return (
<div className="min-h-screen pb-24 bg-background">
{/* Header with Cover */}
<div className="relative h-64">
<Image
src={star.cover || "/placeholder.svg"}
alt={star.name}
fill
className="object-cover"
/>
<Image src={star.cover || "/placeholder.svg"} alt={star.name} fill className="object-cover" />
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-background/50 to-background" />
{/* Back Button */}
<Link href="/" className="absolute top-4 left-4 z-10 w-10 h-10 rounded-full glass flex items-center justify-center">
<Link
href="/"
className="absolute top-4 left-4 z-10 w-10 h-10 rounded-full glass flex items-center justify-center"
>
<ArrowLeft size={20} />
</Link>
@@ -157,12 +155,7 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
{/* Avatar */}
<div className="absolute -bottom-12 left-4 w-24 h-24 rounded-2xl border-4 border-background overflow-hidden">
<Image
src={star.avatar || "/placeholder.svg"}
alt={star.name}
fill
className="object-cover"
/>
<Image src={star.avatar || "/placeholder.svg"} alt={star.name} fill className="object-cover" />
</div>
</div>
@@ -247,7 +240,10 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
</h3>
<div className="space-y-3">
{star.services.map((service: any) => (
<div key={service.id} className="glass-card p-4 rounded-xl relative overflow-hidden group hover:border-primary/30 transition-colors">
<div
key={service.id}
className="glass-card p-4 rounded-xl relative overflow-hidden group hover:border-primary/30 transition-colors"
>
{service.popular && (
<div className="absolute top-2 right-2 px-2 py-0.5 rounded-full bg-primary/20 text-primary text-[10px] font-bold">
@@ -258,7 +254,7 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
🔥
</div>
)}
<div className="flex items-start justify-between mb-2">
<div>
<div className="flex items-center gap-2 mb-1">
@@ -277,7 +273,7 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
</div>
</div>
<Link
<Link
href={`/booking?starId=${params.id}&serviceId=${service.id}`}
className="w-full mt-3 py-2.5 rounded-lg bg-primary text-black font-bold text-center block hover:bg-primary/90 transition-colors"
>
@@ -295,14 +291,21 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
<MessageCircle size={18} className="text-accent" />
</h3>
<Link href="#" className="text-xs text-white/50"></Link>
<Link href="#" className="text-xs text-white/50">
</Link>
</div>
<div className="space-y-3">
{star.reviews.map((review: any, i: number) => (
<div key={i} className="glass-card p-4 rounded-xl">
<div className="flex items-center gap-3 mb-2">
<div className="w-8 h-8 rounded-full bg-muted relative overflow-hidden">
<Image src={review.avatar || "/placeholder.svg"} alt={review.user} fill className="object-cover" />
<Image
src={review.avatar || `/placeholder.svg?height=32&width=32&query=user`}
alt={review.user}
fill
className="object-cover"
/>
</div>
<div className="flex-1">
<div className="flex items-center justify-between">
@@ -333,5 +336,5 @@ export default function StarDetailPage({ params }: { params: { id: string } }) {
</div>
</div>
</div>
);
)
}

View File

@@ -1,48 +1,66 @@
"use client";
"use client"
import Link from "next/link";
import { usePathname } from 'next/navigation';
import { Home, Play, ShoppingBag, User, Globe } from 'lucide-react';
import { cn } from "@/lib/utils";
import Link from "next/link"
import { usePathname } from "next/navigation"
import { Home, Radio, ShoppingBag, User, Globe } from "lucide-react"
import { cn } from "@/lib/utils"
export function MobileNav() {
const pathname = usePathname();
const pathname = usePathname()
const navItems = [
{ href: "/", icon: Home, label: "首页" },
{ href: "/moments", icon: Play, label: "动态" },
{ href: "/planet", icon: Globe, label: "星球" },
{ href: "/moments", icon: Radio, label: "直播" },
{ href: "/planet", icon: Globe, label: "星球", isHighlight: true }, // Mark planet as highlight
{ href: "/mall", icon: ShoppingBag, label: "商城" },
{ href: "/profile", icon: User, label: "我的" },
];
]
return (
<div className="fixed bottom-0 left-0 right-0 z-50 flex justify-center">
<div className="w-full max-w-md glass border-t border-white/10 pb-safe">
<nav className="flex items-center justify-around h-16 px-2">
{navItems.map((item) => {
const isActive = pathname === item.href;
const isActive = pathname === item.href
const isPlanet = item.isHighlight
return (
<Link
key={item.href}
href={item.href}
className={cn(
"flex flex-col items-center justify-center w-full h-full space-y-1 transition-all duration-300",
isActive ? "text-primary" : "text-muted-foreground hover:text-foreground"
"flex flex-col items-center justify-center w-full h-full space-y-1 transition-all duration-300 relative",
isActive ? "text-primary" : "text-muted-foreground hover:text-foreground",
)}
>
<div className={cn("relative p-1 rounded-xl transition-all", isActive && "bg-primary/10")}>
<item.icon size={22} strokeWidth={isActive ? 2.5 : 2} />
{isActive && (
<span className="absolute inset-0 rounded-xl bg-primary/20 blur-lg animate-pulse" />
<div
className={cn(
"relative p-1 rounded-xl transition-all",
isActive && "bg-primary/10",
isPlanet && "scale-125 -translate-y-1", // Planet is 25% larger and lifted
)}
>
<item.icon
size={isPlanet ? 28 : 22} // Planet icon is larger
strokeWidth={isActive ? 2.5 : 2}
className={cn(isPlanet && "text-purple-400")} // Planet has unique purple color
/>
{isActive && <span className="absolute inset-0 rounded-xl bg-primary/20 blur-lg animate-pulse" />}
{isPlanet && !isActive && <span className="absolute inset-0 rounded-xl bg-purple-400/10 blur-md" />}
</div>
<span className="text-[10px] font-medium">{item.label}</span>
<span
className={cn(
"text-[10px] font-medium",
isPlanet && "font-bold text-purple-400", // Planet label is bold and purple
)}
>
{item.label}
</span>
</Link>
);
)
})}
</nav>
</div>
</div>
);
)
}

21
components/ui/input.tsx Normal file
View File

@@ -0,0 +1,21 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
return (
<input
type={type}
data-slot="input"
className={cn(
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
className,
)}
{...props}
/>
)
}
export { Input }

View File

@@ -0,0 +1,58 @@
'use client'
import * as React from 'react'
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
import { cn } from '@/lib/utils'
function ScrollArea({
className,
children,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn('relative', className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
)
}
function ScrollBar({
className,
orientation = 'vertical',
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return (
<ScrollAreaPrimitive.ScrollAreaScrollbar
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
'flex touch-none p-px transition-colors select-none',
orientation === 'vertical' &&
'h-full w-2.5 border-l border-l-transparent',
orientation === 'horizontal' &&
'h-2.5 flex-col border-t border-t-transparent',
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb
data-slot="scroll-area-thumb"
className="bg-border relative flex-1 rounded-full"
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
)
}
export { ScrollArea, ScrollBar }

View File

@@ -0,0 +1,18 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
return (
<textarea
data-slot="textarea"
className={cn(
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className,
)}
{...props}
/>
)
}
export { Textarea }

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 KiB

BIN
public/abstract-energy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
public/avatar-.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
public/avatar-1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
public/avatar-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
public/esports-trophy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
public/gamer-avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
public/gamer-avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
public/gamer-girl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
public/gaming-room-rgb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
public/lol-gameplay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
public/my-avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
public/orc-warrior.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
public/pro-gamer-male.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
public/tactical-gamer.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
public/wow-gameplay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
public/wow-player.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB