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>
106
app/chat/[id]/page.tsx
Normal 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">点击进入直播间一起互动 ></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
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
234
app/hotel/page.tsx
Normal 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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
69
app/moments/create/page.tsx
Normal 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
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
107
app/page.tsx
@@ -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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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">享网咖8折、酒店9折特权</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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 }
|
||||
58
components/ui/scroll-area.tsx
Normal 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 }
|
||||
18
components/ui/textarea.tsx
Normal 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 }
|
||||
BIN
public/abstract-energy-pattern.png
Normal file
|
After Width: | Height: | Size: 736 KiB |
BIN
public/abstract-energy.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
public/abstract-esports-pattern.jpg
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
public/avatar-.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
public/avatar-1.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
public/avatar-2.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/current-user-avatar.png
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
public/cute-female-gamer.jpg
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
public/esports-hotel-cyberpunk-room.jpg
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
public/esports-live-stream-gameplay.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
public/esports-pro-gamer-male-jersey-confident.jpg
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
public/esports-streaming-vertical.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/esports-team-logo.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/esports-tournament-stadium.jpg
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
public/esports-tournament.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/esports-trophy.png
Normal file
|
After Width: | Height: | Size: 677 KiB |
BIN
public/female-streamer-vertical.jpg
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
public/fps-gameplay-intense.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/gamer-avatar.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
public/gamer-avatar.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/gamer-girl-headphones.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/gamer-girl.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/gaming-room-rgb.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/internet-cafe-luxury-interior.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/lol-gameplay-highlight.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
public/lol-gameplay.jpg
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
public/my-avatar.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/orc-warrior.jpg
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
public/pro-gamer-male.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
public/tactical-gamer.jpg
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
public/wow-gameplay.jpg
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
public/wow-player.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |