feat: enhance live room and planet matchmaking features
Add fan group entry on live room; update planet matching with 4 type buttons. Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
119
app/coach/[id]/page.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, Star, Trophy, Users, Shield, Gamepad2, Award, 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 CoachDetailPage({ params }: { params: { id: string } }) {
|
||||
const router = useRouter()
|
||||
|
||||
const handleBook = () => {
|
||||
toast({
|
||||
title: "预约成功",
|
||||
description: "陪练师将在15分钟内联系您",
|
||||
})
|
||||
}
|
||||
|
||||
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-4 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="/avatar-2.jpg" alt="Coach" fill className="object-cover" />
|
||||
<div className="absolute bottom-0 right-0 w-5 h-5 rounded-full bg-green-500 border-2 border-background" />
|
||||
</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-green-500 gap-1 hover:bg-green-600" onClick={handleBook}>
|
||||
<Gamepad2 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>
|
||||
<Award className="text-yellow-400" size={20} />
|
||||
</div>
|
||||
<p className="text-sm text-white/60 mb-3">前LPL职业选手 · 国服第一打野 · 5年教学经验</p>
|
||||
<div className="flex items-center gap-4 text-xs text-white/40">
|
||||
<span className="flex items-center gap-1">
|
||||
<Star size={12} className="text-primary fill-primary" />
|
||||
4.9 评分
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Trophy size={12} className="text-accent" />
|
||||
2456 订单
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users size={12} className="text-secondary" />
|
||||
98% 好评
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div className="space-y-4 mb-6">
|
||||
<h3 className="font-bold text-sm flex items-center gap-2">
|
||||
<Gamepad2 size={16} className="text-green-500" />
|
||||
服务项目
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ name: "单局陪练", price: 80, desc: "边玩边教,即时指导", duration: "1局" },
|
||||
{ name: "5局套餐", price: 360, desc: "系统提升,优惠45元", duration: "5局", hot: true },
|
||||
{ name: "10局特训", price: 650, desc: "深度培训,包上分", duration: "10局" },
|
||||
].map((service, i) => (
|
||||
<div key={i} className="glass-card p-4 rounded-xl relative">
|
||||
{service.hot && (
|
||||
<span className="absolute top-2 right-2 px-2 py-0.5 rounded-full bg-green-500/20 text-green-400 text-[10px] font-bold">
|
||||
最热门
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div>
|
||||
<h4 className="font-bold mb-1">{service.name}</h4>
|
||||
<p className="text-xs text-white/60">{service.desc}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-2xl font-bold text-green-500">{service.price}</div>
|
||||
<div className="text-[10px] text-white/50">元</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trust Badge */}
|
||||
<div className="glass-card p-4 rounded-xl bg-gradient-to-r from-green-500/10 to-transparent border-l-4 border-green-500">
|
||||
<div className="flex items-center gap-3">
|
||||
<Shield size={24} className="text-green-500" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm">平台认证陪练师</h4>
|
||||
<p className="text-xs text-white/60">服务不满意,全额退款</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
208
app/coach/page.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, Trophy, Star, Gamepad2, MessageCircle, Award } 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 CoachPage() {
|
||||
const router = useRouter()
|
||||
|
||||
const handleBookCoach = (coachName: string) => {
|
||||
toast({
|
||||
title: "预约成功",
|
||||
description: `已成功预约${coachName}的陪练服务`,
|
||||
})
|
||||
}
|
||||
|
||||
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 space-y-6">
|
||||
{/* Banner */}
|
||||
<div className="glass-card p-4 rounded-xl border-l-4 border-green-500">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="font-bold text-sm">专业陪练服务</h3>
|
||||
<span className="text-xs text-white/50">1000+ 陪练师</span>
|
||||
</div>
|
||||
<p className="text-xs text-white/60 mb-3">前职业选手、路人王陪你上分,技术提升看得见</p>
|
||||
<div className="grid grid-cols-3 gap-2 text-center">
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-green-500">专业认证</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">实力保障</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-blue-500">快速上分</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">胜率提升</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-purple-500">技术指导</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">边玩边学</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<Tabs defaultValue="hot" className="w-full">
|
||||
<TabsList className="w-full bg-white/5 p-1 rounded-xl">
|
||||
<TabsTrigger value="hot" className="flex-1 text-xs">
|
||||
热门陪练
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="pro" className="flex-1 text-xs">
|
||||
职业选手
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="king" className="flex-1 text-xs">
|
||||
王者大神
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="hot" className="mt-4 space-y-3">
|
||||
{[
|
||||
{
|
||||
name: "职业打野-小明",
|
||||
title: "前LPL职业选手",
|
||||
avatar: "/avatar-2.jpg",
|
||||
games: ["英雄联盟"],
|
||||
rank: "王者2000分",
|
||||
orders: 2456,
|
||||
rating: 4.9,
|
||||
price: 80,
|
||||
tags: ["职业", "打野", "带飞"],
|
||||
online: true,
|
||||
},
|
||||
{
|
||||
name: "上分小助手",
|
||||
title: "国服路人王",
|
||||
avatar: "/avatar-4.jpg",
|
||||
games: ["王者荣耀", "和平精英"],
|
||||
rank: "荣耀王者100星",
|
||||
orders: 1823,
|
||||
rating: 4.8,
|
||||
price: 50,
|
||||
tags: ["温柔", "耐心", "指导"],
|
||||
online: true,
|
||||
},
|
||||
{
|
||||
name: "FPS大神",
|
||||
title: "前职业选手",
|
||||
avatar: "/avatar-5.jpg",
|
||||
games: ["无畏契约", "三角洲"],
|
||||
rank: "超凡入圣",
|
||||
orders: 987,
|
||||
rating: 4.9,
|
||||
price: 100,
|
||||
tags: ["枪法", "意识", "专业"],
|
||||
online: false,
|
||||
},
|
||||
{
|
||||
name: "温柔陪练师",
|
||||
title: "全能型选手",
|
||||
avatar: "/avatar-6.jpg",
|
||||
games: ["王者荣耀", "英雄联盟"],
|
||||
rank: "多区王者",
|
||||
orders: 3201,
|
||||
rating: 5.0,
|
||||
price: 60,
|
||||
tags: ["声音好听", "技术强", "幽默"],
|
||||
online: true,
|
||||
},
|
||||
].map((coach, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="glass-card p-4 rounded-xl hover:bg-white/5 transition-colors cursor-pointer"
|
||||
onClick={() => router.push(`/coach/${i + 1}`)}
|
||||
>
|
||||
<div className="flex items-start gap-3 mb-3">
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 rounded-lg bg-muted relative overflow-hidden">
|
||||
<Image src={coach.avatar || "/placeholder.svg"} alt={coach.name} fill className="object-cover" />
|
||||
</div>
|
||||
{coach.online && (
|
||||
<div className="absolute -bottom-1 -right-1 w-4 h-4 rounded-full bg-green-500 border-2 border-background" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h4 className="font-bold truncate">{coach.name}</h4>
|
||||
{coach.rating === 5.0 && <Award className="w-4 h-4 text-yellow-400" />}
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-white/60 mb-2">{coach.title}</p>
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-white/50 mb-2">
|
||||
<span className="flex items-center gap-1">
|
||||
<Star size={12} className="text-primary fill-primary" />
|
||||
{coach.rating}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Trophy size={12} className="text-accent" />
|
||||
{coach.orders}单
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{coach.tags.map((tag, j) => (
|
||||
<span key={j} className="px-2 py-0.5 rounded-full bg-white/5 text-[10px] text-white/60">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="text-lg font-bold text-green-500">{coach.price}</div>
|
||||
<div className="text-[10px] text-white/50">元/局</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex-1 h-8 text-xs border-white/10 hover:bg-white/5 bg-transparent"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
router.push(`/coach/${i + 1}`)
|
||||
}}
|
||||
>
|
||||
<MessageCircle size={14} className="mr-1" />
|
||||
查看详情
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="flex-1 h-8 text-xs bg-green-500 hover:bg-green-600"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleBookCoach(coach.name)
|
||||
}}
|
||||
>
|
||||
<Gamepad2 size={14} className="mr-1" />
|
||||
立即预约
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="pro" className="mt-4">
|
||||
<div className="text-center py-12 text-white/40 text-sm">职业选手列表加载中...</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="king" className="mt-4">
|
||||
<div className="text-center py-12 text-white/40 text-sm">王者大神列表加载中...</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
127
app/cp/[id]/page.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, Heart, Star, MessageCircle, Shield, Trophy, Users, Gamepad2 } 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 CPDetailPage({ params }: { params: { id: string } }) {
|
||||
const router = useRouter()
|
||||
|
||||
const handleSendRequest = () => {
|
||||
toast({
|
||||
title: "请求已发送",
|
||||
description: "对方收到通知后会尽快回复你",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background pb-24 relative">
|
||||
{/* Hero Section */}
|
||||
<div className="h-48 relative">
|
||||
<Image src="/esports-live-stream-gameplay.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-4 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="/avatar-1.jpg" alt="Avatar" fill className="object-cover" />
|
||||
<div className="absolute bottom-0 right-0 w-5 h-5 rounded-full bg-green-500 border-2 border-background" />
|
||||
</div>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 text-xs gap-1 bg-transparent border-pink-500/50 text-pink-400 hover:bg-pink-500/10"
|
||||
>
|
||||
<Heart size={14} />
|
||||
关注
|
||||
</Button>
|
||||
<Button size="sm" className="h-8 text-xs bg-pink-500 gap-1 hover:bg-pink-600" onClick={handleSendRequest}>
|
||||
<MessageCircle 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-pink-500/20 text-pink-400 text-[10px] border border-pink-500/30 font-bold">
|
||||
女 · 22岁
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/60 mb-3">喜欢玩辅助,找个靠谱的ADC一起开黑~</p>
|
||||
<div className="flex items-center gap-4 text-xs text-white/40">
|
||||
<span className="flex items-center gap-1">
|
||||
<Star size={12} className="text-primary" />
|
||||
星耀段位
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Trophy size={12} className="text-accent" />
|
||||
385 场游戏
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users size={12} className="text-secondary" />
|
||||
68 位好友
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Games */}
|
||||
<div className="space-y-4 mb-6">
|
||||
<h3 className="font-bold text-sm flex items-center gap-2">
|
||||
<Gamepad2 size={16} className="text-primary" />
|
||||
擅长游戏
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{[
|
||||
{ game: "王者荣耀", rank: "星耀I", hero: "瑶、大乔" },
|
||||
{ game: "和平精英", rank: "无敌战神", role: "四排辅助位" },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="glass-card p-3 rounded-xl">
|
||||
<div className="font-bold text-sm mb-1">{item.game}</div>
|
||||
<div className="text-xs text-white/50">{item.rank}</div>
|
||||
<div className="text-xs text-primary mt-1">{item.hero || item.role}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tags */}
|
||||
<div className="space-y-4 mb-6">
|
||||
<h3 className="font-bold text-sm">个人标签</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{["声音好听", "技术不错", "温柔", "有耐心", "爱聊天"].map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="px-3 py-1.5 rounded-full bg-pink-500/10 border border-pink-500/30 text-xs text-pink-400"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trust Badge */}
|
||||
<div className="glass-card p-4 rounded-xl bg-gradient-to-r from-pink-500/10 to-transparent border-l-4 border-pink-500">
|
||||
<div className="flex items-center gap-3">
|
||||
<Shield size={24} className="text-pink-500" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm">平台实名认证</h4>
|
||||
<p className="text-xs text-white/60">真实身份,安全交友</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
207
app/cp/page.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, Heart, Star, MessageCircle, 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 CPPage() {
|
||||
const router = useRouter()
|
||||
|
||||
const handleContact = (cpName: string) => {
|
||||
toast({
|
||||
title: "发送成功",
|
||||
description: `已向${cpName}发送交友请求`,
|
||||
})
|
||||
}
|
||||
|
||||
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">电竞CP</h1>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-6">
|
||||
{/* Banner */}
|
||||
<div className="glass-card p-4 rounded-xl border-l-4 border-pink-500 bg-gradient-to-r from-pink-500/10 to-transparent">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="font-bold text-sm flex items-center gap-2">
|
||||
<Heart className="text-pink-500" size={18} />
|
||||
找个游戏CP
|
||||
</h3>
|
||||
<Sparkles className="text-pink-500 animate-pulse" size={18} />
|
||||
</div>
|
||||
<p className="text-xs text-white/60 mb-3">一起开黑,一起上分,遇见你的电竞知己</p>
|
||||
<div className="grid grid-cols-3 gap-2 text-center">
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-pink-500">声音甜美</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">温柔妹子</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-blue-500">技术强劲</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">带飞大佬</div>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2">
|
||||
<div className="text-xs font-bold text-purple-500">长期陪伴</div>
|
||||
<div className="text-[10px] text-white/50 mt-0.5">稳定组队</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<Tabs defaultValue="all" className="w-full">
|
||||
<TabsList className="w-full bg-white/5 p-1 rounded-xl">
|
||||
<TabsTrigger value="all" className="flex-1 text-xs">
|
||||
全部
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="girl" className="flex-1 text-xs">
|
||||
女生
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="boy" className="flex-1 text-xs">
|
||||
男生
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="all" className="mt-4 space-y-3">
|
||||
{[
|
||||
{
|
||||
name: "甜心辅助",
|
||||
gender: "女",
|
||||
age: 22,
|
||||
avatar: "/avatar-1.jpg",
|
||||
games: ["王者荣耀", "和平精英"],
|
||||
voice: "温柔甜美",
|
||||
rank: "星耀",
|
||||
online: true,
|
||||
tags: ["声音好听", "技术不错", "温柔"],
|
||||
bio: "喜欢玩辅助,找个靠谱的ADC一起开黑~",
|
||||
},
|
||||
{
|
||||
name: "职业打野",
|
||||
gender: "男",
|
||||
age: 24,
|
||||
avatar: "/avatar-2.jpg",
|
||||
games: ["英雄联盟", "无畏契约"],
|
||||
voice: "成熟稳重",
|
||||
rank: "大师",
|
||||
online: true,
|
||||
tags: ["技术流", "有耐心", "幽默"],
|
||||
bio: "前职业选手,带你上分不是梦",
|
||||
},
|
||||
{
|
||||
name: "软萌小姐姐",
|
||||
gender: "女",
|
||||
age: 20,
|
||||
avatar: "/avatar-3.jpg",
|
||||
games: ["王者荣耀", "原神"],
|
||||
voice: "软萌可爱",
|
||||
rank: "钻石",
|
||||
online: false,
|
||||
tags: ["萌妹子", "爱聊天", "佛系"],
|
||||
bio: "游戏菜但是爱玩,找个不嫌弃的~",
|
||||
},
|
||||
{
|
||||
name: "中单刺客",
|
||||
gender: "男",
|
||||
age: 23,
|
||||
avatar: "/avatar-4.jpg",
|
||||
games: ["英雄联盟"],
|
||||
voice: "阳光帅气",
|
||||
rank: "王者",
|
||||
online: true,
|
||||
tags: ["Carry型", "稳定", "负责"],
|
||||
bio: "擅长刺客中单,带妹上分专业户",
|
||||
},
|
||||
].map((cp, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="glass-card p-4 rounded-xl hover:bg-white/5 transition-colors cursor-pointer"
|
||||
onClick={() => router.push(`/cp/${i + 1}`)}
|
||||
>
|
||||
<div className="flex items-start gap-3 mb-3">
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 rounded-lg bg-muted relative overflow-hidden">
|
||||
<Image src={cp.avatar || "/placeholder.svg"} alt={cp.name} fill className="object-cover" />
|
||||
</div>
|
||||
{cp.online && (
|
||||
<div className="absolute -bottom-1 -right-1 w-4 h-4 rounded-full bg-green-500 border-2 border-background" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h4 className="font-bold truncate">{cp.name}</h4>
|
||||
<span
|
||||
className={`px-1.5 py-0.5 rounded text-[10px] ${cp.gender === "女" ? "bg-pink-500/20 text-pink-400" : "bg-blue-500/20 text-blue-400"}`}
|
||||
>
|
||||
{cp.gender} · {cp.age}岁
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-white/50 mb-2">
|
||||
<span className="flex items-center gap-1">
|
||||
<Star size={12} className="text-primary" />
|
||||
{cp.rank}
|
||||
</span>
|
||||
<span>{cp.voice}</span>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-white/60 mb-2 line-clamp-1">{cp.bio}</p>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{cp.tags.map((tag, j) => (
|
||||
<span key={j} className="px-2 py-0.5 rounded-full bg-white/5 text-[10px] text-white/60">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex-1 h-8 text-xs border-white/10 hover:bg-white/5 bg-transparent"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleContact(cp.name)
|
||||
}}
|
||||
>
|
||||
<MessageCircle size={14} className="mr-1" />
|
||||
打个招呼
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="flex-1 h-8 text-xs bg-pink-500 hover:bg-pink-600"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
router.push(`/cp/${i + 1}`)
|
||||
}}
|
||||
>
|
||||
<Heart size={14} className="mr-1" />
|
||||
查看详情
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="girl" className="mt-4">
|
||||
<div className="text-center py-12 text-white/40 text-sm">女生列表加载中...</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="boy" className="mt-4">
|
||||
<div className="text-center py-12 text-white/40 text-sm">男生列表加载中...</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,16 +11,16 @@ 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" },
|
||||
{ id: 1, name: "赛博朋克战士", price: 98, rarity: "传说", sales: 1205, image: "/avatar-cyberpunk.jpg" },
|
||||
{ id: 2, name: "甜心电竞少女", price: 68, rarity: "稀有", sales: 3420, image: "/avatar-cute.jpg" },
|
||||
{ id: 3, name: "暗影刺客", price: 128, rarity: "传说", sales: 856, image: "/avatar-assassin.jpg" },
|
||||
{ id: 4, name: "机甲战神", price: 88, rarity: "史诗", sales: 2100, image: "/avatar-mecha.jpg" },
|
||||
]
|
||||
|
||||
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" },
|
||||
{ id: 101, name: "星空边框", price: 38, type: "边框", image: "/frame-starry.jpg" },
|
||||
{ id: 102, name: "炫光特效", price: 58, type: "特效", image: "/effect-neon.jpg" },
|
||||
{ id: 103, name: "王者徽章", price: 48, type: "徽章", image: "/badge-gold.jpg" },
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -41,7 +41,7 @@ export default function DressUpPage() {
|
||||
{/* 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" />
|
||||
<Image src="/avatar-cyberpunk.jpg" 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>
|
||||
@@ -81,18 +81,13 @@ export default function DressUpPage() {
|
||||
className="glass-card p-3 rounded-xl hover:bg-white/10 transition-colors cursor-pointer"
|
||||
onClick={() =>
|
||||
toast({
|
||||
title: "即将购买",
|
||||
description: `${avatar.name} - ¥${avatar.price}`,
|
||||
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"
|
||||
/>
|
||||
<Image src={avatar.image || "/placeholder.svg"} 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>
|
||||
@@ -118,12 +113,7 @@ export default function DressUpPage() {
|
||||
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"
|
||||
/>
|
||||
<Image src={item.image || "/placeholder.svg"} alt={item.name} fill className="object-cover" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-sm mb-1">{item.name}</h4>
|
||||
|
||||
106
app/events/today/page.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, Clock, Users, Trophy, MapPin } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
|
||||
export default function TodayEventsPage() {
|
||||
const router = useRouter()
|
||||
|
||||
const events = [
|
||||
{
|
||||
id: 1,
|
||||
title: "2025 玩值杯全明星邀请赛",
|
||||
game: "英雄联盟",
|
||||
time: "20:00",
|
||||
status: "即将开始",
|
||||
teams: ["RNG", "EDG"],
|
||||
venue: "上海梅赛德斯奔驰中心",
|
||||
prize: "100万",
|
||||
viewers: "25.6w",
|
||||
image: "/tournament-event-today.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "王者荣耀职业联赛",
|
||||
game: "王者荣耀",
|
||||
time: "18:30",
|
||||
status: "直播中",
|
||||
teams: ["AG超玩会", "QGhappy"],
|
||||
venue: "线上赛",
|
||||
prize: "50万",
|
||||
viewers: "18.2w",
|
||||
image: "/tournament-event-today.jpg",
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 bg-[#0D0D15]/90 backdrop-blur-md border-b border-white/5 px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||
<ArrowLeft size={20} className="text-white" />
|
||||
</button>
|
||||
<h1 className="font-bold text-lg text-white">今日赛事</h1>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-4">
|
||||
{events.map((event) => (
|
||||
<Link
|
||||
key={event.id}
|
||||
href={`/events/${event.id}`}
|
||||
className="block bg-[#1A1A22] rounded-xl overflow-hidden border border-white/5 hover:border-primary/30 transition-all"
|
||||
>
|
||||
<div className="relative aspect-[16/9]">
|
||||
<Image src={event.image || "/placeholder.svg"} alt={event.title} fill className="object-cover" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-transparent to-transparent" />
|
||||
{event.status === "直播中" && (
|
||||
<div className="absolute top-3 left-3 flex items-center gap-1 bg-secondary/90 backdrop-blur-md px-2 py-1 rounded-md text-xs font-bold text-white animate-pulse">
|
||||
<span className="w-2 h-2 rounded-full bg-white" />
|
||||
{event.status}
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute bottom-3 left-3 right-3">
|
||||
<h3 className="font-bold text-white mb-1">{event.title}</h3>
|
||||
<div className="flex items-center gap-2 text-xs text-white/70">
|
||||
<Clock size={14} />
|
||||
{event.time}
|
||||
<Users size={14} className="ml-2" />
|
||||
{event.viewers}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="text-center">
|
||||
<div className="font-bold text-white">{event.teams[0]}</div>
|
||||
<div className="text-xs text-white/50">战队</div>
|
||||
</div>
|
||||
<div className="text-primary font-bold">VS</div>
|
||||
<div className="text-center">
|
||||
<div className="font-bold text-white">{event.teams[1]}</div>
|
||||
<div className="text-xs text-white/50">战队</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="flex items-center gap-1 text-yellow-500 text-sm font-bold">
|
||||
<Trophy size={14} />
|
||||
{event.prize}
|
||||
</div>
|
||||
<div className="text-xs text-white/50">奖金池</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-xs text-white/50">
|
||||
<MapPin size={14} />
|
||||
{event.venue}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</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={`/.jpg?height=100&width=100&query=${guild.logo}`}
|
||||
src={`/guild-logo-${i}.jpg?height=100&width=100&query=${guild.logo}`}
|
||||
alt={guild.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Search, MapPin, Filter, Star, ArrowLeft, ChevronDown } from "lucide-rea
|
||||
import { useRouter } from "next/navigation"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
import Image from "next/image"
|
||||
|
||||
const hotels = [
|
||||
{
|
||||
@@ -16,6 +17,7 @@ const hotels = [
|
||||
distance: "0.8km",
|
||||
tags: ["RTX4090", "电竞椅", "24h餐饮"],
|
||||
district: "西湖区/黄龙体育中心",
|
||||
image: "esports hotel room neon lights gaming setup luxury",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -26,6 +28,7 @@ const hotels = [
|
||||
distance: "1.2km",
|
||||
tags: ["五黑房", "投影", "免费停车"],
|
||||
district: "拱墅区/万达广场",
|
||||
image: "gaming apartment interior cozy modern pc setup",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -36,6 +39,7 @@ const hotels = [
|
||||
distance: "2.5km",
|
||||
tags: ["情侣房", "PS5", "VR体验"],
|
||||
district: "滨江区/网易园区",
|
||||
image: "vr gaming room couple setup futuristic lighting",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -49,6 +53,7 @@ const cafes = [
|
||||
distance: "0.5km",
|
||||
tags: ["3080显卡", "特权公馆", "现磨咖啡"],
|
||||
district: "武林广场商圈",
|
||||
image: "internet cafe luxury rows of computers modern design",
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
@@ -59,6 +64,7 @@ const cafes = [
|
||||
distance: "1.8km",
|
||||
tags: ["赛事级配置", "美女陪玩", "独立包间"],
|
||||
district: "城西银泰",
|
||||
image: "esports arena internet cafe private room",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -131,99 +137,136 @@ export default function HotelPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="hotel" className="mt-0 px-4 py-2 space-y-2 min-h-[50vh]">
|
||||
<TabsContent value="hotel" className="mt-0 px-4 py-2 space-y-3 min-h-[50vh]">
|
||||
{/* Added Header for Recommendations */}
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-sm font-bold text-white/90">热门推荐</span>
|
||||
</div>
|
||||
|
||||
{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"
|
||||
className="flex gap-3 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>
|
||||
)}
|
||||
{/* Added Image on the left */}
|
||||
<div className="relative w-24 h-24 rounded-lg overflow-hidden shrink-0 bg-muted">
|
||||
<Image
|
||||
src={`/.jpg?key=hotel-${hotel.id}&height=200&width=200&query=${hotel.image}`}
|
||||
alt={hotel.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
{hotel.id === 1 && (
|
||||
<div className="absolute top-0 left-0 bg-red-500 text-white text-[10px] px-1.5 py-0.5 rounded-br-lg font-bold">
|
||||
热门
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex flex-col justify-between min-w-0">
|
||||
<div>
|
||||
<div className="flex items-start justify-between mb-1">
|
||||
<h3 className="font-bold text-base truncate pr-2">{hotel.name}</h3>
|
||||
</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" />
|
||||
<div className="flex items-center text-orange-400 font-bold bg-orange-500/10 px-1 rounded">
|
||||
<Star size={10} 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>
|
||||
<span className="text-primary font-bold">{hotel.reviews}条评论</span>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-white/40 truncate mb-1">
|
||||
{hotel.district} | {hotel.distance}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{hotel.tags.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="px-1.5 py-0.5 rounded text-[10px] border border-white/10 text-white/50 bg-white/5"
|
||||
>
|
||||
{tag}
|
||||
</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">
|
||||
|
||||
<div className="flex items-end justify-between mt-1">
|
||||
<div className="flex items-baseline gap-0.5 text-red-500">
|
||||
<span className="text-xs font-bold">¥</span>
|
||||
<span className="text-2xl font-bold leading-none">{hotel.price}</span>
|
||||
<span className="text-xl font-bold leading-none">{hotel.price}</span>
|
||||
<span className="text-xs text-white/50 ml-1 font-normal">起</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 className="px-3 py-1 bg-primary text-white text-xs rounded-full 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]">
|
||||
<TabsContent value="cafe" className="mt-0 px-4 py-2 space-y-3 min-h-[50vh]">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-sm font-bold text-white/90">附近好店</span>
|
||||
</div>
|
||||
|
||||
{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"
|
||||
className="flex gap-3 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>
|
||||
{/* Added Image on the left */}
|
||||
<div className="relative w-24 h-24 rounded-lg overflow-hidden shrink-0 bg-muted">
|
||||
<Image
|
||||
src={`/.jpg?key=cafe-${cafe.id}&height=200&width=200&query=${cafe.image}`}
|
||||
alt={cafe.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex flex-col justify-between min-w-0">
|
||||
<div>
|
||||
<h3 className="font-bold text-base mb-1 truncate">{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" />
|
||||
<div className="flex items-center text-orange-400 font-bold bg-orange-500/10 px-1 rounded">
|
||||
<Star size={10} 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>
|
||||
<span className="text-white/40">人均 ¥{(cafe.price * 3).toFixed(0)}</span>
|
||||
</div>
|
||||
<div className="text-xs text-white/40 truncate mb-1">
|
||||
{cafe.district} | {cafe.distance}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{cafe.tags.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="text-[10px] px-1.5 py-0.5 rounded bg-primary/5 text-primary border border-primary/20"
|
||||
>
|
||||
{tag}
|
||||
</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">
|
||||
|
||||
<div className="flex items-end justify-between mt-1">
|
||||
<div className="flex items-baseline gap-0.5">
|
||||
<span className="text-xs text-white/60">¥</span>
|
||||
<span className="text-xl font-bold text-primary leading-none">{cafe.price}</span>
|
||||
<span className="text-lg font-bold text-primary leading-none">{cafe.price}</span>
|
||||
<span className="text-xs text-white/40">/时</span>
|
||||
</div>
|
||||
<button className="px-3 py-1 bg-white/10 text-white text-xs rounded-full font-bold active:scale-95 transition-transform hover:bg-white/20">
|
||||
抢座
|
||||
</button>
|
||||
</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>
|
||||
|
||||
@@ -2,13 +2,39 @@
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ArrowLeft, Heart, Share2, Gift, Send, MoreHorizontal } from "lucide-react"
|
||||
import { ArrowLeft, Heart, Share2, Gift, Send, MoreHorizontal, Users, MessageCircle } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
|
||||
export default function LiveRoomPage({ params }: { params: { id: string } }) {
|
||||
const router = useRouter()
|
||||
const [comment, setComment] = useState("")
|
||||
const [isFollowed, setIsFollowed] = useState(false)
|
||||
const [showCommunity, setShowCommunity] = useState(false)
|
||||
|
||||
const players = [
|
||||
{ id: "1", name: "GM-远洋", avatar: "/avatar-1.jpg", title: "国服第一盲僧", fans: "46K", partyMembers: 1205 },
|
||||
{ id: "2", name: "卡若", avatar: "/avatar-2.jpg", title: "职业战队退役", fans: "32K", partyMembers: 856 },
|
||||
{ id: "3", name: "小柠檬", avatar: "/avatar-3.jpg", title: "人皮话多", fans: "18K", partyMembers: 432 },
|
||||
{ id: "4", name: "阿伟", avatar: "/avatar-4.jpg", title: "绝地求生刚枪王", fans: "25K", partyMembers: 678 },
|
||||
{ id: "5", name: "七七", avatar: "/avatar-5.jpg", title: "甜美声优", fans: "52K", partyMembers: 1502 },
|
||||
{ id: "6", name: "大魔王", avatar: "/avatar-6.jpg", title: "中单法王", fans: "38K", partyMembers: 945 },
|
||||
]
|
||||
|
||||
const currentPlayer = players.find((p) => p.id === params.id) || players[0]
|
||||
|
||||
const handleJoinParty = () => {
|
||||
toast({
|
||||
title: "加入成功",
|
||||
description: `已加入 ${currentPlayer.name} 的粉丝派对群`,
|
||||
})
|
||||
setShowCommunity(false)
|
||||
setTimeout(() => {
|
||||
router.push(`/chat/${params.id}`)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full bg-black relative overflow-hidden flex flex-col">
|
||||
@@ -26,10 +52,10 @@ export default function LiveRoomPage({ params }: { params: { id: string } }) {
|
||||
</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} />
|
||||
<Image src={currentPlayer.avatar || "/placeholder.svg"} alt="Avatar" width={32} height={32} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xs font-bold text-white">GM-远洋</h3>
|
||||
<h3 className="text-xs font-bold text-white">{currentPlayer.name}</h3>
|
||||
<p className="text-[10px] text-white/70">12.5w观看</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,12 +71,7 @@ export default function LiveRoomPage({ params }: { params: { id: string } }) {
|
||||
<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}
|
||||
/>
|
||||
<Image src={`/avatar-${i}.jpg`} alt="Viewer" width={32} height={32} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -60,6 +81,85 @@ export default function LiveRoomPage({ params }: { params: { id: string } }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog open={showCommunity} onOpenChange={setShowCommunity}>
|
||||
<DialogTrigger asChild>
|
||||
<button className="absolute right-4 top-1/3 z-30 flex flex-col items-center gap-1 bg-gradient-to-b from-primary/80 to-purple-600/80 backdrop-blur-md px-3 py-4 rounded-2xl border border-white/20 shadow-lg shadow-primary/30 hover:scale-105 transition-transform">
|
||||
<Users size={24} className="text-white" />
|
||||
<span className="text-[10px] text-white font-bold">粉丝群</span>
|
||||
<span className="text-[8px] text-white/70">{currentPlayer.partyMembers}</span>
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="bg-zinc-900 border-white/10 text-white max-w-sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<MessageCircle className="text-primary" size={20} />
|
||||
{currentPlayer.name} 的派对群
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
{/* 主播信息卡片 */}
|
||||
<div className="flex items-center gap-4 p-4 bg-white/5 rounded-xl border border-white/10">
|
||||
<div className="w-16 h-16 rounded-full overflow-hidden border-2 border-primary">
|
||||
<Image
|
||||
src={currentPlayer.avatar || "/placeholder.svg"}
|
||||
alt={currentPlayer.name}
|
||||
width={64}
|
||||
height={64}
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-lg">{currentPlayer.name}</h4>
|
||||
<p className="text-xs text-white/60">{currentPlayer.title}</p>
|
||||
<div className="flex items-center gap-3 mt-2 text-xs text-white/50">
|
||||
<span className="flex items-center gap-1">
|
||||
<Heart size={12} className="text-pink-500" />
|
||||
{currentPlayer.fans} 粉丝
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users size={12} className="text-blue-500" />
|
||||
{currentPlayer.partyMembers} 群成员
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 群介绍 */}
|
||||
<div className="p-4 bg-gradient-to-r from-primary/10 to-purple-500/10 rounded-xl border border-primary/20">
|
||||
<h5 className="font-bold text-sm mb-2 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||
派对正在进行中
|
||||
</h5>
|
||||
<p className="text-xs text-white/70 leading-relaxed">
|
||||
加入 {currentPlayer.name} 的专属粉丝派对群,与主播和其他粉丝实时互动、语音开黑、参与专属福利活动!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 群特权 */}
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{[
|
||||
{ icon: "🎁", text: "专属礼物" },
|
||||
{ icon: "🎮", text: "组队开黑" },
|
||||
{ icon: "💬", text: "语音聊天" },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="flex flex-col items-center gap-1 p-3 bg-white/5 rounded-lg">
|
||||
<span className="text-xl">{item.icon}</span>
|
||||
<span className="text-[10px] text-white/70">{item.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleJoinParty}
|
||||
className="w-full bg-gradient-to-r from-primary to-purple-600 hover:from-primary/90 hover:to-purple-600/90 font-bold"
|
||||
>
|
||||
<Users size={16} className="mr-2" />
|
||||
立即加入派对群
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Main Content Spacer */}
|
||||
<div className="flex-1" />
|
||||
|
||||
|
||||
281
app/mall/companion/page.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
import { Star, Play, Mic, Video, Gamepad2, GraduationCap } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
|
||||
export default function CompanionPage() {
|
||||
const router = useRouter()
|
||||
const [activeTab, setActiveTab] = useState("play") // play | learn
|
||||
|
||||
const games = [
|
||||
{ name: "全部", id: "all" },
|
||||
{ name: "英雄联盟", id: "lol" },
|
||||
{ name: "王者荣耀", id: "hok" },
|
||||
{ name: "无畏契约", id: "val" },
|
||||
{ name: "和平精英", id: "pubg" },
|
||||
{ name: "原神", id: "genshin" },
|
||||
]
|
||||
const [activeGame, setActiveGame] = useState("all")
|
||||
|
||||
const companions = [
|
||||
{
|
||||
id: 1,
|
||||
name: "GM-远洋",
|
||||
title: "国服第一盲僧",
|
||||
price: 50,
|
||||
unit: "局",
|
||||
image: "/avatar-1.jpg",
|
||||
tags: ["技术流", "风趣", "职业退役"],
|
||||
game: "lol",
|
||||
rating: 5.0,
|
||||
orders: 1205,
|
||||
voice: true,
|
||||
video: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "七七",
|
||||
title: "甜美声优",
|
||||
price: 40,
|
||||
unit: "小时",
|
||||
image: "/avatar-5.jpg",
|
||||
tags: ["声优", "聊天", "辅助"],
|
||||
game: "hok",
|
||||
rating: 4.9,
|
||||
orders: 890,
|
||||
voice: true,
|
||||
video: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "卡若",
|
||||
title: "前职业选手",
|
||||
price: 80,
|
||||
unit: "局",
|
||||
image: "/avatar-2.jpg",
|
||||
tags: ["包赢", "教学", "高分局"],
|
||||
game: "lol",
|
||||
rating: 5.0,
|
||||
orders: 3400,
|
||||
voice: true,
|
||||
video: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "小柠檬",
|
||||
title: "人皮话多",
|
||||
price: 30,
|
||||
unit: "局",
|
||||
image: "/avatar-3.jpg",
|
||||
tags: ["娱乐", "开心", "相声"],
|
||||
game: "val",
|
||||
rating: 4.8,
|
||||
orders: 560,
|
||||
voice: true,
|
||||
video: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "大魔王",
|
||||
title: "中单法王",
|
||||
price: 100,
|
||||
unit: "局",
|
||||
image: "/avatar-6.jpg",
|
||||
tags: ["技术教学", "上分", "严肃"],
|
||||
game: "lol",
|
||||
rating: 5.0,
|
||||
orders: 8800,
|
||||
voice: true,
|
||||
video: true,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "阿伟",
|
||||
title: "绝地刚枪王",
|
||||
price: 60,
|
||||
unit: "小时",
|
||||
image: "/avatar-4.jpg",
|
||||
tags: ["带妹", "吃鸡", "指挥"],
|
||||
game: "pubg",
|
||||
rating: 4.9,
|
||||
orders: 1100,
|
||||
voice: true,
|
||||
video: false,
|
||||
},
|
||||
]
|
||||
|
||||
const courses = [
|
||||
{
|
||||
id: 1,
|
||||
title: "中单瑞兹顶级教学",
|
||||
author: "Faker",
|
||||
price: 199,
|
||||
image: "/course-1.jpg",
|
||||
students: 12000,
|
||||
game: "lol",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "下路对线细节全解",
|
||||
author: "Uzi",
|
||||
price: 199,
|
||||
image: "/course-2.jpg",
|
||||
students: 35000,
|
||||
game: "lol",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "上单压制力养成",
|
||||
author: "TheShy",
|
||||
price: 159,
|
||||
image: "/course-3.jpg",
|
||||
students: 8900,
|
||||
game: "lol",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "瓦罗兰特特工教学",
|
||||
author: "康康",
|
||||
price: 99,
|
||||
image: "/course-4.jpg",
|
||||
students: 4500,
|
||||
game: "val",
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70 hover:text-white">
|
||||
返回
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">明星陪玩 & 课程</h1>
|
||||
</header>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="px-4 mt-2">
|
||||
<div className="bg-white/5 p-1 rounded-xl flex">
|
||||
<button
|
||||
className={`flex-1 py-2 rounded-lg text-sm font-bold flex items-center justify-center gap-2 transition-all ${activeTab === "play" ? "bg-primary text-black shadow-lg shadow-primary/20" : "text-white/50 hover:text-white"}`}
|
||||
onClick={() => setActiveTab("play")}
|
||||
>
|
||||
<Gamepad2 size={16} />
|
||||
明星陪玩
|
||||
</button>
|
||||
<button
|
||||
className={`flex-1 py-2 rounded-lg text-sm font-bold flex items-center justify-center gap-2 transition-all ${activeTab === "learn" ? "bg-secondary text-white shadow-lg shadow-secondary/20" : "text-white/50 hover:text-white"}`}
|
||||
onClick={() => setActiveTab("learn")}
|
||||
>
|
||||
<GraduationCap size={16} />
|
||||
大师课程
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Game Filter */}
|
||||
<div className="px-4 mt-4 overflow-x-auto no-scrollbar pb-2">
|
||||
<div className="flex gap-2">
|
||||
{games.map((g) => (
|
||||
<button
|
||||
key={g.id}
|
||||
onClick={() => setActiveGame(g.id)}
|
||||
className={`px-4 py-1.5 rounded-full text-xs font-bold whitespace-nowrap transition-colors ${activeGame === g.id ? "bg-white text-black" : "bg-white/5 text-white/50 hover:bg-white/10"}`}
|
||||
>
|
||||
{g.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="px-4 mt-2 space-y-4">
|
||||
{activeTab === "play" ? (
|
||||
<div className="grid gap-3">
|
||||
{companions
|
||||
.filter((c) => activeGame === "all" || c.game === activeGame)
|
||||
.map((c) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className="glass-card p-3 rounded-xl flex gap-3 cursor-pointer hover:bg-white/5 transition-colors group"
|
||||
onClick={() => router.push(`/live/${c.id}`)}
|
||||
>
|
||||
<div className="relative w-24 h-24 rounded-lg overflow-hidden shrink-0">
|
||||
<Image src={c.image || "/placeholder.svg"} alt={c.name} fill className="object-cover" />
|
||||
<div className="absolute top-1 left-1 bg-black/60 backdrop-blur-sm px-1.5 py-0.5 rounded flex items-center gap-1 text-[10px] text-white font-bold">
|
||||
<Star size={8} className="text-yellow-500 fill-yellow-500" />
|
||||
{c.rating}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col justify-between py-0.5">
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="font-bold text-sm flex items-center gap-1">
|
||||
{c.name}
|
||||
{c.voice && <Mic size={10} className="text-green-400" />}
|
||||
{c.video && <Video size={10} className="text-blue-400" />}
|
||||
</h3>
|
||||
<span className="text-[10px] text-white/40">{c.orders}单</span>
|
||||
</div>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">{c.title}</p>
|
||||
<div className="flex flex-wrap gap-1 mt-1.5">
|
||||
{c.tags.map((tag, i) => (
|
||||
<span key={i} className="text-[10px] bg-white/5 px-1.5 py-0.5 rounded text-white/50">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<div className="flex items-end gap-1">
|
||||
<span className="text-lg font-bold text-secondary">{c.price}</span>
|
||||
<span className="text-xs text-white/50 mb-1">币/{c.unit}</span>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-7 text-xs px-3 font-bold bg-white/10 hover:bg-primary hover:text-black"
|
||||
>
|
||||
立即下单
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4">
|
||||
{courses
|
||||
.filter((c) => activeGame === "all" || c.game === activeGame)
|
||||
.map((c) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className="glass-card p-0 rounded-xl overflow-hidden cursor-pointer group"
|
||||
onClick={() => toast({ title: "购买成功", description: "课程已添加至学习中心" })}
|
||||
>
|
||||
<div className="relative aspect-video">
|
||||
<Image src={c.image || "/placeholder.svg"} alt={c.title} fill className="object-cover" />
|
||||
<div className="absolute inset-0 bg-black/30 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Play size={40} className="text-white fill-white" />
|
||||
</div>
|
||||
<div className="absolute bottom-2 left-2 px-2 py-1 bg-black/60 backdrop-blur-sm rounded text-[10px] font-bold">
|
||||
{c.author} 亲授
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3">
|
||||
<h3 className="font-bold text-sm mb-1">{c.title}</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-white/50">{c.students} 人已学</span>
|
||||
<span className="text-primary font-bold">¥{c.price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
58
app/mall/courses/page.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
import { Play, Users } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
|
||||
export default function CoursesPage() {
|
||||
const router = useRouter()
|
||||
const courses = [
|
||||
{ id: 1, title: "Faker 中单瑞兹教学", author: "Faker", price: 199, image: "/course-1.jpg", students: 12000 },
|
||||
{ id: 2, title: "Uzi 下路对线细节", author: "Uzi", price: 199, image: "/course-2.jpg", students: 35000 },
|
||||
{ id: 3, title: "TheShy 上单压制力", author: "TheShy", price: 159, image: "/course-3.jpg", students: 8900 },
|
||||
{ id: 4, title: "10分钟学会云顶吃鸡", author: "云顶之弈", price: 49, image: "/course-4.jpg", students: 5000 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70">
|
||||
返回
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">同款课程</h1>
|
||||
</header>
|
||||
<div className="p-4 space-y-4">
|
||||
{courses.map((c) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className="glass-card p-3 rounded-xl flex gap-3 cursor-pointer hover:bg-white/10"
|
||||
onClick={() => toast({ title: "购买成功", description: "课程已添加至学习中心" })}
|
||||
>
|
||||
<div className="relative w-32 aspect-video rounded-lg overflow-hidden shrink-0">
|
||||
<Image src={c.image || "/placeholder.svg"} alt={c.title} fill className="object-cover" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/30">
|
||||
<Play size={24} className="text-white/80" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col justify-between">
|
||||
<h3 className="font-bold text-sm">{c.title}</h3>
|
||||
<div className="flex items-center gap-2 text-xs text-white/50">
|
||||
<span className="bg-white/10 px-1.5 py-0.5 rounded text-white/70">{c.author}</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users size={10} /> {c.students}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-1">
|
||||
<span className="text-primary font-bold">¥{c.price}</span>
|
||||
<Button size="sm" className="h-6 text-[10px]">
|
||||
购买
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
56
app/mall/gear/page.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
"use client"
|
||||
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 GearPage() {
|
||||
const router = useRouter()
|
||||
const products = [
|
||||
{ id: 1, name: "罗技 G Pro X Superlight", price: 899, image: "/gear-mouse.jpg", tag: "职业首选" },
|
||||
{ id: 2, name: "雷蛇 猎魂光蛛 V3", price: 1299, image: "/gear-keyboard.jpg", tag: "光轴科技" },
|
||||
{ id: 3, name: "卓威 XL2546K", price: 3499, image: "/gear-monitor.jpg", tag: "360Hz" },
|
||||
{ id: 4, name: "HyperX Cloud II", price: 599, image: "/gear-headset.jpg", tag: "7.1声道" },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70">
|
||||
返回
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">电竞外设</h1>
|
||||
</header>
|
||||
<div className="p-4 grid grid-cols-2 gap-4">
|
||||
{products.map((p) => (
|
||||
<div
|
||||
key={p.id}
|
||||
className="glass-card rounded-xl overflow-hidden group cursor-pointer"
|
||||
onClick={() => toast({ title: "加入购物车", description: p.name })}
|
||||
>
|
||||
<div className="relative aspect-square bg-white">
|
||||
<Image
|
||||
src={p.image || "/placeholder.svg"}
|
||||
alt={p.name}
|
||||
fill
|
||||
className="object-contain p-4 group-hover:scale-105 transition-transform"
|
||||
/>
|
||||
<div className="absolute top-2 left-2 bg-red-500 text-white text-[10px] px-2 py-0.5 rounded font-bold">
|
||||
{p.tag}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3">
|
||||
<h3 className="font-bold text-sm text-white truncate mb-1">{p.name}</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-primary font-bold">¥{p.price}</span>
|
||||
<Button size="sm" className="h-6 text-[10px] px-2">
|
||||
购买
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -9,20 +9,24 @@ import {
|
||||
Gift,
|
||||
Coins,
|
||||
BookOpen,
|
||||
GraduationCap,
|
||||
Monitor,
|
||||
Hotel,
|
||||
Coffee,
|
||||
Trophy,
|
||||
User,
|
||||
Keyboard,
|
||||
Wallet,
|
||||
Gamepad2,
|
||||
Gem,
|
||||
} 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 Link from "next/link" // Import Link
|
||||
import Link from "next/link"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
export default function MallPage() {
|
||||
const { wallet, pay, recharge } = useAppContext()
|
||||
const { wallet, pay } = useAppContext()
|
||||
const router = useRouter()
|
||||
|
||||
const handleBuy = (price: number, currency: "diamonds" | "coins", name: string, type: "product" | "service") => {
|
||||
pay(price, currency, name, type)
|
||||
@@ -32,9 +36,12 @@ export default function MallPage() {
|
||||
<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 justify-between">
|
||||
<h1 className="font-bold text-lg">商城</h1>
|
||||
<h1 className="font-bold text-lg">商城中心</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 px-3 py-1 rounded-full bg-white/5 border border-white/10">
|
||||
<div
|
||||
className="flex items-center gap-2 px-3 py-1 rounded-full bg-white/5 border border-white/10"
|
||||
onClick={() => router.push("/recharge")}
|
||||
>
|
||||
<Diamond size={14} className="text-primary" />
|
||||
<span className="text-xs font-bold">{wallet.diamonds}</span>
|
||||
</div>
|
||||
@@ -47,25 +54,24 @@ export default function MallPage() {
|
||||
|
||||
{/* Banner */}
|
||||
<div className="px-4 mt-4">
|
||||
<div className="w-full h-32 rounded-2xl bg-gradient-to-r from-purple-600 to-blue-600 relative overflow-hidden flex items-center px-6">
|
||||
<div
|
||||
className="w-full h-32 rounded-2xl bg-gradient-to-r from-purple-600 to-blue-600 relative overflow-hidden flex items-center px-6 shadow-lg shadow-purple-500/20 cursor-pointer"
|
||||
onClick={() => router.push("/recharge")}
|
||||
>
|
||||
<div className="relative z-10">
|
||||
<h2 className="text-xl font-bold text-white mb-1">首充双倍</h2>
|
||||
<p className="text-xs text-white/80 mb-3">限时特惠 赠送绝版头像框</p>
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-white text-purple-600 hover:bg-white/90 h-7 text-xs font-bold"
|
||||
onClick={() => recharge(100)}
|
||||
>
|
||||
<Button size="sm" className="bg-white text-purple-600 hover:bg-white/90 h-7 text-xs font-bold">
|
||||
立即充值
|
||||
</Button>
|
||||
</div>
|
||||
<div className="absolute right-0 bottom-0 w-32 h-32 opacity-50">
|
||||
<Diamond className="w-full h-full text-white/20 rotate-12 translate-x-8 translate-y-8" />
|
||||
<Gem className="w-full h-full text-white/20 rotate-12 translate-x-8 translate-y-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top Categories - Expanded as requested */}
|
||||
{/* Top Categories - 8 Items */}
|
||||
<div className="px-4 mt-6">
|
||||
<h3 className="text-sm font-bold mb-3 flex items-center gap-2">
|
||||
<Sparkles size={16} className="text-yellow-400" />
|
||||
@@ -74,22 +80,22 @@ export default function MallPage() {
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
{[
|
||||
{
|
||||
name: "知识付费",
|
||||
icon: BookOpen,
|
||||
name: "明星陪玩",
|
||||
icon: Gamepad2,
|
||||
color: "text-blue-400",
|
||||
bg: "bg-blue-500/10",
|
||||
href: "/my-courses",
|
||||
href: "/mall/companion?type=play",
|
||||
},
|
||||
{
|
||||
name: "陪玩课程",
|
||||
icon: GraduationCap,
|
||||
name: "大师课程",
|
||||
icon: BookOpen,
|
||||
color: "text-pink-400",
|
||||
bg: "bg-pink-500/10",
|
||||
href: "/creator/apply",
|
||||
href: "/mall/companion?type=course",
|
||||
},
|
||||
{
|
||||
name: "电竞外设",
|
||||
icon: Monitor,
|
||||
icon: Keyboard,
|
||||
color: "text-purple-400",
|
||||
bg: "bg-purple-500/10",
|
||||
href: "/mall/gear",
|
||||
@@ -102,7 +108,7 @@ export default function MallPage() {
|
||||
href: "/hotel",
|
||||
},
|
||||
{
|
||||
name: "附近网吧",
|
||||
name: "附近网咖",
|
||||
icon: Coffee,
|
||||
color: "text-cyan-400",
|
||||
bg: "bg-cyan-500/10",
|
||||
@@ -120,26 +126,26 @@ export default function MallPage() {
|
||||
icon: User,
|
||||
color: "text-green-400",
|
||||
bg: "bg-green-500/10",
|
||||
href: "/services/account",
|
||||
href: "/services/account?tab=buy",
|
||||
},
|
||||
{
|
||||
name: "更多服务",
|
||||
icon: Sparkles,
|
||||
color: "text-white/40",
|
||||
bg: "bg-white/5",
|
||||
href: "#",
|
||||
name: "账号金融",
|
||||
icon: Wallet,
|
||||
color: "text-indigo-400",
|
||||
bg: "bg-indigo-500/10",
|
||||
href: "/services/account?tab=finance",
|
||||
},
|
||||
].map((item, i) => (
|
||||
<Link
|
||||
key={i}
|
||||
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"
|
||||
className="glass-card p-3 rounded-xl flex flex-col items-center gap-2 cursor-pointer hover:bg-white/10 transition-colors border border-white/5 hover:border-white/10"
|
||||
>
|
||||
<div className={`w-10 h-10 rounded-full ${item.bg} flex items-center justify-center`}>
|
||||
<item.icon className={item.color} size={20} />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<h3 className="text-[10px] font-bold whitespace-nowrap">{item.name}</h3>
|
||||
<h3 className="text-[10px] font-bold whitespace-nowrap text-white/90">{item.name}</h3>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
@@ -149,25 +155,25 @@ export default function MallPage() {
|
||||
<Tabs defaultValue="dress" className="mt-6">
|
||||
<div className="px-4">
|
||||
<TabsList className="w-full bg-white/5 p-1 rounded-xl">
|
||||
<TabsTrigger value="dress" className="flex-1 text-xs">
|
||||
装扮
|
||||
<TabsTrigger value="dress" className="flex-1 text-xs font-bold">
|
||||
头像装扮
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="gift" className="flex-1 text-xs">
|
||||
礼物
|
||||
<TabsTrigger value="gift" className="flex-1 text-xs font-bold">
|
||||
礼物道具
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="exchange" className="flex-1 text-xs">
|
||||
兑换
|
||||
<TabsTrigger value="exchange" className="flex-1 text-xs font-bold">
|
||||
福利兑换
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<TabsContent value="dress" className="mt-4">
|
||||
{/* Categories */}
|
||||
<div className="flex items-center gap-4 px-4 overflow-x-auto no-scrollbar mb-4">
|
||||
{["推荐", "头像框", "聊天气泡", "入场特效", "座驾"].map((cat, i) => (
|
||||
<div className="flex items-center gap-3 px-4 overflow-x-auto no-scrollbar mb-4">
|
||||
{["热门推荐", "限定头像", "动态边框", "入场特效", "聊天气泡"].map((cat, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`whitespace-nowrap text-xs font-medium px-3 py-1.5 rounded-full transition-colors ${i === 0 ? "bg-primary text-white" : "bg-white/5 text-white/50"}`}
|
||||
className={`whitespace-nowrap text-xs font-bold px-3 py-1.5 rounded-full transition-colors ${i === 0 ? "bg-primary text-black" : "bg-white/5 text-white/50"}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
@@ -177,30 +183,69 @@ export default function MallPage() {
|
||||
{/* Grid */}
|
||||
<div className="px-4 grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{ name: "巫瞻猫影", price: 99, type: "frame", color: "from-purple-500/20 to-blue-500/20" },
|
||||
{ name: "椰树风情", price: 128, type: "frame", color: "from-green-500/20 to-yellow-500/20" },
|
||||
{ name: "黑白天使", price: 299, type: "wing", color: "from-gray-500/20 to-white/20" },
|
||||
{ name: "敦煌飞天", price: 520, type: "ring", color: "from-orange-500/20 to-red-500/20" },
|
||||
{ name: "恶魔之眼", price: 199, type: "frame", color: "from-red-900/20 to-black/20" },
|
||||
{ name: "小熊猫", price: 68, type: "frame", color: "from-orange-300/20 to-yellow-300/20" },
|
||||
{
|
||||
name: "巫瞻猫影",
|
||||
price: 99,
|
||||
type: "frame",
|
||||
color: "from-purple-500/20 to-blue-500/20",
|
||||
query: "Esports avatar frame with cat and witch theme, purple and blue gradients, mystical symbols",
|
||||
},
|
||||
{
|
||||
name: "椰树风情",
|
||||
price: 128,
|
||||
type: "frame",
|
||||
color: "from-green-500/20 to-yellow-500/20",
|
||||
query: "Tropical esports avatar frame with palm trees and summer vibes, green and yellow",
|
||||
},
|
||||
{
|
||||
name: "黑白天使",
|
||||
price: 299,
|
||||
type: "wing",
|
||||
color: "from-gray-500/20 to-white/20",
|
||||
query: "Esports angel wings avatar decoration, black and white contrasting feathers, divine effect",
|
||||
},
|
||||
{
|
||||
name: "敦煌飞天",
|
||||
price: 520,
|
||||
type: "ring",
|
||||
color: "from-orange-500/20 to-red-500/20",
|
||||
query: "Chinese Dunhuang flying apsara themed avatar ring, orange and red, traditional art style",
|
||||
},
|
||||
{
|
||||
name: "恶魔之眼",
|
||||
price: 199,
|
||||
type: "frame",
|
||||
color: "from-red-900/20 to-black/20",
|
||||
query: "Dark esports avatar frame with demonic red eye motif, black and red, ominous vibe",
|
||||
},
|
||||
{
|
||||
name: "小熊猫",
|
||||
price: 68,
|
||||
type: "frame",
|
||||
color: "from-orange-300/20 to-yellow-300/20",
|
||||
query: "Cute esports red panda avatar frame, orange and yellow, playful design",
|
||||
},
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="glass-card rounded-xl p-4 flex flex-col items-center gap-3 group relative overflow-hidden"
|
||||
className="glass-card rounded-xl p-4 flex flex-col items-center gap-3 group relative overflow-hidden border border-white/5 hover:border-primary/30"
|
||||
>
|
||||
<div
|
||||
className={`absolute inset-0 bg-gradient-to-br ${item.color} opacity-0 group-hover:opacity-100 transition-opacity duration-500`}
|
||||
/>
|
||||
|
||||
<div className="relative w-20 h-20 flex items-center justify-center">
|
||||
<div className="w-16 h-16 rounded-full border-2 border-dashed border-white/20 flex items-center justify-center relative z-10">
|
||||
<Sparkles className="text-white/20" />
|
||||
</div>
|
||||
<div className="absolute inset-0 rounded-full border-4 border-primary/30 blur-md animate-pulse-slow" />
|
||||
{/* Placeholder for local image if we had one, otherwise Sparkles */}
|
||||
<img
|
||||
src={`/.jpg?key=w3tzs&height=80&width=80&query=${encodeURIComponent(item.query)}`}
|
||||
alt={item.name}
|
||||
className="w-16 h-16 object-contain group-hover:scale-110 transition-transform"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-full border-4 border-primary/30 blur-md animate-pulse-slow opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</div>
|
||||
|
||||
<div className="text-center relative z-10">
|
||||
<h3 className="font-bold text-sm mb-1">{item.name}</h3>
|
||||
<h3 className="font-bold text-sm mb-1 text-white/90">{item.name}</h3>
|
||||
<div className="flex items-center justify-center gap-1 text-primary">
|
||||
<Diamond size={12} />
|
||||
<span className="text-xs font-bold">{item.price}</span>
|
||||
@@ -219,6 +264,7 @@ export default function MallPage() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="gift" className="mt-4 px-4">
|
||||
{/* Gift Content - Simplified */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ name: "棒棒糖", price: 10, icon: "🍭" },
|
||||
@@ -230,10 +276,10 @@ export default function MallPage() {
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="glass-card rounded-xl p-3 flex flex-col items-center gap-2 hover:bg-white/10 cursor-pointer transition-colors"
|
||||
className="glass-card rounded-xl p-3 flex flex-col items-center gap-2 hover:bg-white/10 cursor-pointer transition-colors border border-white/5"
|
||||
onClick={() => handleBuy(item.price, "diamonds", item.name, "product")}
|
||||
>
|
||||
<div className="text-3xl">{item.icon}</div>
|
||||
<div className="text-3xl filter drop-shadow-lg">{item.icon}</div>
|
||||
<div className="text-center">
|
||||
<h3 className="text-xs font-bold">{item.name}</h3>
|
||||
<div className="flex items-center justify-center gap-1 text-primary mt-1">
|
||||
@@ -247,6 +293,7 @@ export default function MallPage() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="exchange" className="mt-4 px-4">
|
||||
{/* Exchange Content */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{ name: "10元话费券", price: 1000, icon: Gift },
|
||||
@@ -254,7 +301,7 @@ export default function MallPage() {
|
||||
{ name: "改名卡", price: 2000, icon: Crown },
|
||||
{ name: "双倍经验卡", price: 500, icon: Zap },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="glass-card rounded-xl p-4 flex flex-col items-center gap-3">
|
||||
<div key={i} className="glass-card rounded-xl p-4 flex flex-col items-center gap-3 border border-white/5">
|
||||
<div className="w-12 h-12 rounded-full bg-yellow-500/10 flex items-center justify-center">
|
||||
<item.icon className="text-yellow-500" size={24} />
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client" // Convert to client component
|
||||
|
||||
import { Trophy, Search, Heart, MessageCircle, Share2, RefreshCcw } from "lucide-react"
|
||||
import { Trophy, Search, Heart, MessageCircle, Share2 } 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
|
||||
import { useRouter } from "next/navigation" // Added useRouter
|
||||
|
||||
const liveStreams = [
|
||||
{
|
||||
@@ -49,9 +49,9 @@ const moments = [
|
||||
{
|
||||
id: 1,
|
||||
user: "GM-远洋",
|
||||
avatar: "/gamer-girl.jpg",
|
||||
avatar: "gamer_girl_avatar_cute", // Fixed hardcoded path to keyword
|
||||
desc: "可惜不是你喜欢的甜妹音 😷 #电竞少女 #英雄联盟 #日常",
|
||||
video: "/esports-streaming-vertical.jpg",
|
||||
video: "esports_streaming_vertical_setup", // Fixed path
|
||||
likes: "1.2w",
|
||||
comments: 458,
|
||||
isLive: true,
|
||||
@@ -61,9 +61,9 @@ const moments = [
|
||||
{
|
||||
id: 2,
|
||||
user: "卡若",
|
||||
avatar: "/pro-gamer-male.jpg",
|
||||
avatar: "pro_gamer_male_avatar", // Fixed hardcoded path
|
||||
desc: "中单刺客教学,这波操作你学会了吗? #英雄联盟 #教学",
|
||||
video: "/lol-gameplay-highlight.jpg",
|
||||
video: "lol_gameplay_highlight_screen", // Fixed path
|
||||
likes: "8.5k",
|
||||
comments: 230,
|
||||
isLive: false,
|
||||
@@ -73,9 +73,9 @@ const moments = [
|
||||
{
|
||||
id: 3,
|
||||
user: "魔兽老张",
|
||||
avatar: "/wow-player.jpg",
|
||||
avatar: "wow_orc_player_avatar", // Fixed hardcoded path
|
||||
desc: "ICC 25人H 教授打法细节讲解 #魔兽世界 #WLK",
|
||||
video: "/placeholder.svg?height=600&width=400",
|
||||
video: "wow_raid_boss_fight", // Fixed path
|
||||
likes: "3.2k",
|
||||
comments: 120,
|
||||
isLive: false,
|
||||
@@ -83,8 +83,26 @@ const moments = [
|
||||
},
|
||||
]
|
||||
|
||||
const mixedFeed = [
|
||||
...moments.map((m) => ({ ...m, type: "video" })),
|
||||
...liveStreams.map((l) => ({
|
||||
id: l.id + 1000,
|
||||
user: l.user,
|
||||
avatar: l.avatar,
|
||||
desc: l.title,
|
||||
video: l.cover, // This will be used for the background image
|
||||
likes: l.viewers,
|
||||
comments: 99,
|
||||
isLive: true,
|
||||
type: "live",
|
||||
roomId: l.id, // Added roomId for navigation
|
||||
})),
|
||||
].sort(() => Math.random() - 0.5)
|
||||
|
||||
export default function MomentsPage() {
|
||||
const [activeTab, setActiveTab] = useState("live")
|
||||
const router = useRouter() // Initialize router
|
||||
const [activeTab, setActiveTab] = useState("recommend") // Changed default to recommend
|
||||
|
||||
const handleLike = () => toast({ title: "点赞成功", description: "已添加到我喜欢的视频" })
|
||||
const handleShare = () => toast({ title: "分享成功", description: "链接已复制到剪贴板" })
|
||||
const handleGift = () => toast({ title: "礼物发送成功", description: "主播已收到您的心意" })
|
||||
@@ -95,8 +113,8 @@ export default function MomentsPage() {
|
||||
return (
|
||||
<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">
|
||||
<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 pointer-events-none">
|
||||
<div className="flex items-center justify-center gap-6 text-lg font-bold shadow-sm pointer-events-auto">
|
||||
<button
|
||||
onClick={() => setActiveTab("follow")}
|
||||
className={`${activeTab === "follow" ? "text-white scale-110" : "text-white/50"} transition-all`}
|
||||
@@ -104,129 +122,57 @@ export default function MomentsPage() {
|
||||
关注
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("live")}
|
||||
className={`${activeTab === "live" ? "text-white scale-110" : "text-white/50"} transition-all`}
|
||||
onClick={() => setActiveTab("recommend")} // Merged Live & Video into Recommend
|
||||
className={`${activeTab === "recommend" ? "text-white scale-110" : "text-white/50"} transition-all`}
|
||||
>
|
||||
直播
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("video")}
|
||||
className={`${activeTab === "video" ? "text-white scale-110" : "text-white/50"} transition-all`}
|
||||
>
|
||||
视频
|
||||
推荐
|
||||
</button>
|
||||
</div>
|
||||
<Search className="absolute right-4 top-4 text-white/80 w-6 h-6" />
|
||||
<Search className="absolute right-4 top-4 text-white/80 w-6 h-6 pointer-events-auto" />
|
||||
</div>
|
||||
|
||||
{/* 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>
|
||||
|
||||
<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={`/.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 className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-black/80" />
|
||||
|
||||
{/* 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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
)}
|
||||
|
||||
{activeTab === "video" && (
|
||||
<div className="flex-1 overflow-y-auto snap-y snap-mandatory no-scrollbar relative bg-black">
|
||||
{activeTab === "recommend" && (
|
||||
<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">
|
||||
{/* Rendering Mixed Feed */}
|
||||
{mixedFeed.map((item, i) => (
|
||||
<div key={i} className="h-full w-full snap-start relative bg-zinc-900 group">
|
||||
<Image
|
||||
src={`/.jpg?key=dsvkk&height=800&width=400&query=${moment.video || "esports_highlight"}`}
|
||||
alt="Video"
|
||||
src={`/feed-${item.type}-${item.id}.jpg?height=800&width=400&query=${item.video} ${item.type === "live" ? "live stream" : ""}`}
|
||||
alt="Content"
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-black/60" />
|
||||
|
||||
{item.type === "live" && (
|
||||
<>
|
||||
<div className="absolute top-20 left-4 px-2 py-1 rounded bg-primary/90 backdrop-blur-sm flex items-center gap-1 animate-pulse z-20">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-white" />
|
||||
<span className="text-xs font-bold">直播中</span>
|
||||
</div>
|
||||
<div
|
||||
className="absolute inset-0 z-10 flex items-center justify-center bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer"
|
||||
onClick={() => router.push(`/live/${item.roomId || item.id}`)}
|
||||
>
|
||||
<div className="px-6 py-3 bg-primary rounded-full font-bold text-white shadow-lg transform hover:scale-105 transition-transform flex items-center gap-2">
|
||||
<span>点击进入直播间</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 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="absolute right-2 bottom-24 flex flex-col items-center gap-6 z-20 pointer-events-none">
|
||||
{" "}
|
||||
{/* Added pointer-events-none to prevent blocking click */}
|
||||
<div className="flex flex-col items-center gap-1 pointer-events-auto">
|
||||
{" "}
|
||||
{/* Re-enable pointer events for buttons */}
|
||||
<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"}`}
|
||||
src={`/.jpg?key=avatar-${item.id}&height=50&width=50&query=${item.avatar}`}
|
||||
alt="User"
|
||||
fill
|
||||
className="object-cover"
|
||||
@@ -237,27 +183,29 @@ export default function MomentsPage() {
|
||||
</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>
|
||||
<Heart size={28} className="text-white drop-shadow-md" />
|
||||
<span className="text-xs font-bold shadow-black drop-shadow-md">{item.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>
|
||||
<MessageCircle size={28} className="text-white drop-shadow-md" />
|
||||
<span className="text-xs font-bold shadow-black drop-shadow-md">{item.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>
|
||||
<Share2 size={28} className="text-white drop-shadow-md" />
|
||||
<span className="text-xs font-bold shadow-black drop-shadow-md">分享</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">
|
||||
<div className="absolute left-4 bottom-24 w-3/4 z-20">
|
||||
<h3 className="font-bold text-lg mb-2 drop-shadow-md">@{item.user}</h3>
|
||||
<p className="text-sm opacity-90 leading-relaxed drop-shadow-md line-clamp-3">{item.desc}</p>
|
||||
{/* @ts-ignore */}
|
||||
{item.hasCourse && (
|
||||
<div className="mt-3 inline-flex items-center gap-2 bg-black/40 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>
|
||||
{/* @ts-ignore */}
|
||||
<span className="text-xs font-bold text-yellow-100">同款课程: {item.courseTitle}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -265,6 +213,12 @@ export default function MomentsPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "follow" && (
|
||||
<div className="flex items-center justify-center h-full text-white/50">
|
||||
<p>暂无关注内容</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
481
app/page.tsx
@@ -1,11 +1,14 @@
|
||||
"use client" // Convert to client component
|
||||
"use client"
|
||||
|
||||
import { Search, Bell, Flame, Trophy, Users, Star, Sword, Crosshair, Crown } from "lucide-react"
|
||||
import { Search, Bell, Flame, Trophy, Users, Sword, Crosshair, Crown } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { toast } from "@/hooks/use-toast" // Import toast
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
export default function HomePage() {
|
||||
const router = useRouter()
|
||||
|
||||
const handleJoinTeam = () => {
|
||||
toast({
|
||||
title: "申请已发送",
|
||||
@@ -13,78 +16,218 @@ export default function HomePage() {
|
||||
})
|
||||
}
|
||||
|
||||
const starPlayers = [
|
||||
{
|
||||
id: "1",
|
||||
name: "GM-远洋",
|
||||
title: "国服第一盲僧",
|
||||
price: "50币/局",
|
||||
image: "/avatar-1.jpg",
|
||||
isLive: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "卡若",
|
||||
title: "职业战队退役",
|
||||
price: "80币/局",
|
||||
image: "/avatar-2.jpg",
|
||||
isLive: true,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "小柠檬",
|
||||
title: "人皮话多",
|
||||
price: "30币/局",
|
||||
image: "/avatar-3.jpg",
|
||||
isLive: false,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "阿伟",
|
||||
title: "绝地求生刚枪王",
|
||||
price: "60币/局",
|
||||
image: "/avatar-4.jpg",
|
||||
isLive: false,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen pb-24">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary to-accent flex items-center justify-center">
|
||||
<span className="font-bold text-black text-xs">WZ</span>
|
||||
<div className="flex flex-col min-h-screen pb-24 bg-background">
|
||||
{/* Refined Header */}
|
||||
<header className="sticky top-0 z-40 bg-[#0D0D15]/80 backdrop-blur-md border-b border-white/5 px-4 py-3 flex items-center justify-between transition-all duration-300">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative w-8 h-8 rounded-lg overflow-hidden border border-white/10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-600 to-blue-600 opacity-80" />
|
||||
<div className="absolute inset-0 flex items-center justify-center font-black italic text-white text-xs">
|
||||
WZ
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="font-bold text-lg tracking-tight">玩值电竞</h1>
|
||||
<h1 className="font-bold text-lg tracking-tight text-white">玩值电竞</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button className="p-2 rounded-full bg-white/5 hover:bg-white/10 transition-colors">
|
||||
<Search size={20} className="text-white/80" />
|
||||
<button className="w-9 h-9 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/10 transition-colors border border-white/5">
|
||||
<Search size={18} className="text-white/70" />
|
||||
</button>
|
||||
<Link href="/messages" className="p-2 rounded-full bg-white/5 hover:bg-white/10 transition-colors relative">
|
||||
<Bell size={20} className="text-white/80" />
|
||||
<span className="absolute top-1.5 right-1.5 w-2 h-2 bg-secondary rounded-full border border-background" />
|
||||
<Link
|
||||
href="/messages"
|
||||
className="w-9 h-9 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/10 transition-colors relative border border-white/5"
|
||||
>
|
||||
<Bell size={18} className="text-white/70" />
|
||||
<span className="absolute top-2 right-2.5 w-1.5 h-1.5 bg-red-500 rounded-full ring-2 ring-black" />
|
||||
</Link>
|
||||
{/* </CHANGE> */}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-6">
|
||||
{/* Hero Banner */}
|
||||
<div className="relative w-full aspect-[16/9] rounded-2xl overflow-hidden group">
|
||||
<div className="p-4 space-y-8">
|
||||
{/* Hero Banner - Kept unchanged as requested */}
|
||||
<div
|
||||
className="relative w-full aspect-[16/9] rounded-2xl overflow-hidden group cursor-pointer shadow-2xl shadow-purple-500/10"
|
||||
onClick={() => router.push("/events/today")}
|
||||
>
|
||||
<Image
|
||||
src="/esports-tournament-stadium-neon-lights-crowd.jpg"
|
||||
alt="Tournament"
|
||||
src="/tournament-event-today.jpg"
|
||||
alt="Today's Tournament"
|
||||
fill
|
||||
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 p-4 w-full">
|
||||
<div className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-secondary/80 backdrop-blur-md text-[10px] font-bold mb-2">
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-[#0D0D15] via-transparent to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 p-5 w-full">
|
||||
<div className="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-secondary/90 backdrop-blur-md text-[10px] font-bold mb-2 shadow-lg shadow-secondary/20">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
|
||||
LIVE
|
||||
</div>
|
||||
<h2 className="text-xl font-bold leading-tight mb-1">2025 玩值杯全明星邀请赛</h2>
|
||||
<p className="text-sm text-white/70">顶级战队集结,争夺百万奖金池</p>
|
||||
<h2 className="text-xl font-bold leading-tight mb-1 text-white">2025 玩值杯全明星邀请赛</h2>
|
||||
<p className="text-xs text-white/70">顶级战队集结,争夺百万奖金池</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* WoW Section */}
|
||||
<section className="relative overflow-hidden rounded-2xl border border-yellow-500/20">
|
||||
<div className="absolute inset-0 bg-[url('/wow-bg.jpg')] bg-cover bg-center opacity-40" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/90 to-transparent" />
|
||||
<div className="relative p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
{/* Industry Recommendations - Meituan Style V4 (Refined) */}
|
||||
<section className="space-y-4">
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<h3 className="font-bold text-lg flex items-center gap-2 text-white">
|
||||
<Flame size={18} className="text-red-500 fill-red-500" />
|
||||
热门推荐
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Link
|
||||
href="/hotel"
|
||||
className="bg-[#1A1A22] p-4 rounded-xl flex items-center justify-between group cursor-pointer hover:bg-[#20202A] transition-colors border border-white/5 shadow-lg"
|
||||
>
|
||||
<div>
|
||||
<h4 className="font-bold text-base text-white mb-1.5 group-hover:text-yellow-400 transition-colors">
|
||||
电竞酒店
|
||||
</h4>
|
||||
<p className="text-[10px] text-white/40 group-hover:text-white/60">高端配置 · 组队开黑</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-full bg-yellow-500/10 flex items-center justify-center group-hover:scale-110 transition-transform border border-yellow-500/20">
|
||||
<Trophy size={20} className="text-yellow-500" />
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
href="/hotel"
|
||||
className="bg-[#1A1A22] p-4 rounded-xl flex items-center justify-between group cursor-pointer hover:bg-[#20202A] transition-colors border border-white/5 shadow-lg"
|
||||
>
|
||||
<div>
|
||||
<h4 className="font-bold text-base text-white mb-1.5 group-hover:text-blue-400 transition-colors">
|
||||
附近网咖
|
||||
</h4>
|
||||
<p className="text-[10px] text-white/40 group-hover:text-white/60">特权网吧 · 极速体验</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-full bg-blue-500/10 flex items-center justify-center group-hover:scale-110 transition-transform border border-blue-500/20">
|
||||
<Users size={20} className="text-blue-500" />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Game Icons Grid */}
|
||||
<div className="grid grid-cols-4 gap-4 px-2">
|
||||
{[
|
||||
{
|
||||
name: "英雄联盟",
|
||||
icon: "Sword",
|
||||
color: "text-blue-400",
|
||||
bg: "bg-blue-500/10",
|
||||
border: "border-blue-500/20",
|
||||
link: "/zone/lol",
|
||||
},
|
||||
{
|
||||
name: "无畏契约",
|
||||
icon: "Crosshair",
|
||||
color: "text-red-400",
|
||||
bg: "bg-red-500/10",
|
||||
border: "border-red-500/20",
|
||||
link: "/zone/val",
|
||||
},
|
||||
{
|
||||
name: "王者荣耀",
|
||||
icon: "Crown",
|
||||
color: "text-yellow-400",
|
||||
bg: "bg-yellow-500/10",
|
||||
border: "border-yellow-500/20",
|
||||
link: "/zone/hok",
|
||||
},
|
||||
{
|
||||
name: "CS:GO",
|
||||
icon: "Target",
|
||||
color: "text-orange-400",
|
||||
bg: "bg-orange-500/10",
|
||||
border: "border-orange-500/20",
|
||||
link: "/zone/csgo",
|
||||
},
|
||||
].map((game, i) => (
|
||||
<Link href={game.link} key={i} className="flex flex-col items-center gap-2 group cursor-pointer">
|
||||
<div
|
||||
className={`w-14 h-14 rounded-2xl ${game.bg} ${game.border} border flex items-center justify-center group-hover:scale-105 transition-transform relative overflow-hidden shadow-lg`}
|
||||
>
|
||||
<div className={`absolute inset-0 bg-current opacity-5 ${game.color}`} />
|
||||
{game.icon === "Sword" && <Sword className={game.color} size={26} />}
|
||||
{game.icon === "Crosshair" && <Crosshair className={game.color} size={26} />}
|
||||
{game.icon === "Crown" && <Crown className={game.color} size={26} />}
|
||||
{game.icon === "Target" && <Crosshair className={game.color} size={26} />}
|
||||
</div>
|
||||
<span className="text-xs font-medium text-white/60 group-hover:text-white transition-colors">
|
||||
{game.name}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* WoW Section - Refined Card Style */}
|
||||
<section className="relative overflow-hidden rounded-2xl border border-yellow-500/20 bg-[#12121A]">
|
||||
<div className="absolute inset-0 bg-[url('/wow-bg.jpg')] bg-cover bg-center opacity-20 mix-blend-overlay" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 to-transparent" />
|
||||
<div className="relative p-5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-bold text-lg flex items-center gap-2 text-yellow-500">
|
||||
<Sword size={18} />
|
||||
魔兽世界专区
|
||||
</h3>
|
||||
<Link href="/wow" className="text-xs text-yellow-500/70 hover:text-yellow-500">
|
||||
进入艾泽拉斯 >
|
||||
<Link
|
||||
href="/zone/wow"
|
||||
className="text-xs text-yellow-500/70 hover:text-yellow-500 flex items-center gap-1"
|
||||
>
|
||||
进入艾泽拉斯 <span className="text-[10px]">></span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="glass-card p-3 rounded-xl border-l-2 border-yellow-500/50">
|
||||
<div className="bg-black/40 backdrop-blur-sm p-3 rounded-xl border-l-2 border-yellow-500/50 hover:bg-black/60 transition-colors">
|
||||
<h4 className="font-bold text-sm text-yellow-100">金团招募</h4>
|
||||
<p className="text-[10px] text-white/60 mt-1">ICC 25人H全通团,来强力DPS</p>
|
||||
<p className="text-[10px] text-white/50 mt-1 line-clamp-1">ICC 25人H全通团,来强力DPS</p>
|
||||
<button
|
||||
className="mt-2 w-full py-1 rounded bg-yellow-600/20 text-yellow-500 text-xs font-bold hover:bg-yellow-600/30"
|
||||
className="mt-3 w-full py-1.5 rounded-lg bg-yellow-600/10 text-yellow-500 text-xs font-bold hover:bg-yellow-600/20 border border-yellow-600/20 transition-colors"
|
||||
onClick={() => toast({ title: "即将开放", description: "金团招募功能正在内测中" })}
|
||||
>
|
||||
查看详情
|
||||
</button>
|
||||
</div>
|
||||
<div className="glass-card p-3 rounded-xl border-l-2 border-blue-500/50">
|
||||
<div className="bg-black/40 backdrop-blur-sm p-3 rounded-xl border-l-2 border-blue-500/50 hover:bg-black/60 transition-colors">
|
||||
<h4 className="font-bold text-sm text-blue-100">大秘境车队</h4>
|
||||
<p className="text-[10px] text-white/60 mt-1">20层低保来个奶,车头已就位</p>
|
||||
<p className="text-[10px] text-white/50 mt-1 line-clamp-1">20层低保来个奶,车头已就位</p>
|
||||
<button
|
||||
className="mt-2 w-full py-1 rounded bg-blue-600/20 text-blue-500 text-xs font-bold hover:bg-blue-600/30"
|
||||
className="mt-3 w-full py-1.5 rounded-lg bg-blue-600/10 text-blue-500 text-xs font-bold hover:bg-blue-600/20 border border-blue-600/20 transition-colors"
|
||||
onClick={handleJoinTeam}
|
||||
>
|
||||
立即上车
|
||||
@@ -94,248 +237,44 @@ export default function HomePage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Game Icons */}
|
||||
<div className="grid grid-cols-4 gap-3 px-2">
|
||||
{[
|
||||
{ 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) => (
|
||||
<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-xs font-medium text-white/80 group-hover:text-white transition-colors">
|
||||
{game.name}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Industry Recommendations */}
|
||||
<section className="space-y-3 mt-2">
|
||||
{/* Star Players Section */}
|
||||
<section className="space-y-4">
|
||||
<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" />
|
||||
热门推荐
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Link
|
||||
href="/hotel"
|
||||
className="glass-card p-0 rounded-xl border-0 relative overflow-hidden group cursor-pointer aspect-[4/3]"
|
||||
>
|
||||
<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>
|
||||
</Link>
|
||||
<Link
|
||||
href="/hotel"
|
||||
className="glass-card p-0 rounded-xl border-0 relative overflow-hidden group cursor-pointer aspect-[4/3]"
|
||||
>
|
||||
<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>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Club Recommendations */}
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="font-bold text-lg flex items-center gap-2">
|
||||
<Trophy size={18} className="text-primary" />
|
||||
推荐俱乐部
|
||||
</h3>
|
||||
<Link href="#" className="text-xs text-white/50 hover:text-primary transition-colors">
|
||||
查看全部
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{
|
||||
name: "洲越电竞",
|
||||
status: "即将开业",
|
||||
id: "50310",
|
||||
members: 26,
|
||||
tags: ["三角洲", "无畏契约"],
|
||||
logo: "esports_team_blue_logo",
|
||||
},
|
||||
{
|
||||
name: "机枪电竞",
|
||||
status: "火热招募",
|
||||
id: "50130",
|
||||
members: 60,
|
||||
tags: ["LOL", "云顶"],
|
||||
logo: "esports_team_red_logo",
|
||||
},
|
||||
{
|
||||
name: "东东电竞",
|
||||
status: "官方自营",
|
||||
id: "88888",
|
||||
members: 1205,
|
||||
tags: ["全能", "陪玩"],
|
||||
logo: "esports_team_gold_logo",
|
||||
},
|
||||
].map((club, i) => (
|
||||
<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 border border-white/10">
|
||||
<Image
|
||||
src={`/.jpg?key=6lzl1&height=100&width=100&query=${club.logo}`}
|
||||
alt={club.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h4 className="font-bold truncate">{club.name}</h4>
|
||||
<div className="flex -space-x-1">
|
||||
{club.tags.map((tag, j) => (
|
||||
<div
|
||||
key={j}
|
||||
className="w-5 h-5 rounded-full bg-muted border border-card flex items-center justify-center text-[8px] overflow-hidden"
|
||||
>
|
||||
{tag[0]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-white/50 mb-2">{club.status}</p>
|
||||
<div className="flex items-center gap-3 text-[10px] text-white/40">
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="font-mono">ID:</span> {club.id}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users size={10} /> {club.members}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Star Players / Streamers */}
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="font-bold text-lg flex items-center gap-2">
|
||||
<Star size={18} className="text-secondary" />
|
||||
<h3 className="font-bold text-lg flex items-center gap-2 text-white">
|
||||
<Flame size={18} className="text-secondary" />
|
||||
明星大神
|
||||
</h3>
|
||||
<Link href="#" className="text-xs text-white/50 hover:text-secondary transition-colors">
|
||||
<Link href="/mall/companion" className="text-xs text-white/50 hover:text-secondary transition-colors">
|
||||
查看全部
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{[
|
||||
{
|
||||
id: "1",
|
||||
name: "GM-远洋",
|
||||
title: "国服第一盲僧",
|
||||
price: "50币/局",
|
||||
image: "professional esports player male headset focused",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "卡若",
|
||||
title: "职业战队退役",
|
||||
price: "80币/局",
|
||||
image: "esports pro gamer male jersey confident",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "小柠檬",
|
||||
title: "人皮话多",
|
||||
price: "30币/局",
|
||||
image: "cute female gamer streamer headphones smiling",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "阿伟",
|
||||
title: "绝地求生刚枪王",
|
||||
price: "60币/局",
|
||||
image: "fps gamer male intense focus tactical",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "七七",
|
||||
title: "甜美声优",
|
||||
price: "40币/局",
|
||||
image: "anime style female gamer kawaii pink",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "大魔王",
|
||||
title: "中单法王",
|
||||
price: "100币/局",
|
||||
image: "legendary esports player male champion trophy",
|
||||
},
|
||||
].map((star, i) => (
|
||||
{starPlayers.map((star) => (
|
||||
<Link
|
||||
key={i}
|
||||
href={`/star/${star.id}`}
|
||||
className="glass-card rounded-xl overflow-hidden group cursor-pointer"
|
||||
key={star.id}
|
||||
href={`/live/${star.id}`}
|
||||
className="relative aspect-[3/4] rounded-xl overflow-hidden group cursor-pointer border border-white/5 shadow-lg"
|
||||
>
|
||||
<div className="relative aspect-[3/4]">
|
||||
<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"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-transparent to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 w-full p-3">
|
||||
<h4 className="font-bold text-sm">{star.name}</h4>
|
||||
<p className="text-[10px] text-white/70 mb-1">{star.title}</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-bold text-primary">{star.price}</span>
|
||||
<button className="w-6 h-6 rounded-full bg-secondary flex items-center justify-center">
|
||||
<Flame size={12} className="text-white" />
|
||||
</button>
|
||||
</div>
|
||||
<Image
|
||||
src={star.image || "/placeholder.svg"}
|
||||
alt={star.name}
|
||||
fill
|
||||
className="object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-transparent to-transparent" />
|
||||
|
||||
{star.isLive && (
|
||||
<div className="absolute top-2 left-2 flex items-center gap-1 bg-secondary/90 backdrop-blur-md px-2 py-0.5 rounded-md text-[10px] font-bold text-white border border-white/20 animate-pulse shadow-lg shadow-secondary/30">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-white" />
|
||||
直播中
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="absolute bottom-0 left-0 w-full p-3 space-y-1">
|
||||
<h4 className="font-bold text-sm text-white truncate">{star.name}</h4>
|
||||
<p className="text-[10px] text-white/70 truncate">{star.title}</p>
|
||||
<div className="text-xs font-bold text-primary">{star.price}</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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, Heart, Sparkles, Filter, Trophy, Crosshair } from "lucide-react"
|
||||
import { Mic, Users, Gamepad2, Headphones, Heart, Sparkles, Filter, Trophy, Crosshair, Star } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { useState } from "react"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
@@ -29,10 +29,11 @@ export default function PlanetPage() {
|
||||
star: {
|
||||
name: "GM-远洋",
|
||||
title: "国服第一盲僧 · 电竞明星",
|
||||
avatar: "/esports-pro-gamer-male-jersey-confident.jpg",
|
||||
avatar: "/avatar-1.jpg",
|
||||
viewers: "12.5w",
|
||||
tags: ["LOL", "技术流"],
|
||||
roomId: 1001,
|
||||
route: "/star/1",
|
||||
},
|
||||
guild: {
|
||||
name: "东东电竞公会",
|
||||
@@ -41,22 +42,25 @@ export default function PlanetPage() {
|
||||
viewers: "1205",
|
||||
tags: ["全能", "培训"],
|
||||
roomId: 1002,
|
||||
route: "/guild/东东电竞",
|
||||
},
|
||||
cp: {
|
||||
name: "甜心辅助",
|
||||
title: "温柔妹子 · 找电竞CP",
|
||||
avatar: "/cute-gamer-girl.jpg",
|
||||
avatar: "/avatar-5.jpg",
|
||||
viewers: "8.3w",
|
||||
tags: ["王者", "温柔"],
|
||||
roomId: 1003,
|
||||
route: "/cp/1",
|
||||
},
|
||||
coach: {
|
||||
name: "职业陪练-小明",
|
||||
title: "前职业选手 · 游戏陪练",
|
||||
avatar: "/game-coach-avatar.jpg",
|
||||
avatar: "/avatar-4.jpg",
|
||||
viewers: "3.2w",
|
||||
tags: ["陪练", "上分"],
|
||||
roomId: 1004,
|
||||
route: "/coach/1",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -69,11 +73,16 @@ export default function PlanetPage() {
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
router.push(`/chat/${result.roomId}`)
|
||||
router.push(result.route)
|
||||
}, 1500)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const handleCategoryChange = (category: MatchCategory) => {
|
||||
setSelectedCategory(category)
|
||||
setMatchResult(null) // 切换时清除之前的匹配结果
|
||||
}
|
||||
|
||||
const handleJoinParty = (title: string) => {
|
||||
toast({
|
||||
title: "正在加入派对",
|
||||
@@ -94,6 +103,19 @@ export default function PlanetPage() {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const getCategoryLabel = () => {
|
||||
switch (selectedCategory) {
|
||||
case "star":
|
||||
return "电竞明星"
|
||||
case "guild":
|
||||
return "主播公会"
|
||||
case "cp":
|
||||
return "电竞CP"
|
||||
case "coach":
|
||||
return "游戏陪练"
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pb-24 pt-6 px-4">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
@@ -173,7 +195,52 @@ export default function PlanetPage() {
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="voice" className="mt-0">
|
||||
<div className="flex flex-col items-center justify-center py-12 space-y-8">
|
||||
<div className="flex flex-col items-center justify-center py-8 space-y-6">
|
||||
<div className="grid grid-cols-4 gap-2 w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleCategoryChange("star")}
|
||||
className={`flex flex-col h-auto py-3 border-white/10 bg-white/5 hover:bg-white/10 hover:border-primary/50 transition-all ${
|
||||
selectedCategory === "star" ? "border-primary bg-primary/20 ring-2 ring-primary/50" : ""
|
||||
}`}
|
||||
>
|
||||
<Star className={`w-5 h-5 mb-1 ${selectedCategory === "star" ? "text-primary" : "text-yellow-500"}`} />
|
||||
<span className="text-[10px]">电竞明星</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleCategoryChange("guild")}
|
||||
className={`flex flex-col h-auto py-3 border-white/10 bg-white/5 hover:bg-white/10 hover:border-blue-500/50 transition-all ${
|
||||
selectedCategory === "guild" ? "border-blue-500 bg-blue-500/20 ring-2 ring-blue-500/50" : ""
|
||||
}`}
|
||||
>
|
||||
<Users className={`w-5 h-5 mb-1 ${selectedCategory === "guild" ? "text-blue-400" : "text-blue-500"}`} />
|
||||
<span className="text-[10px]">主播公会</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleCategoryChange("cp")}
|
||||
className={`flex flex-col h-auto py-3 border-white/10 bg-white/5 hover:bg-white/10 hover:border-pink-500/50 transition-all ${
|
||||
selectedCategory === "cp" ? "border-pink-500 bg-pink-500/20 ring-2 ring-pink-500/50" : ""
|
||||
}`}
|
||||
>
|
||||
<Heart className={`w-5 h-5 mb-1 ${selectedCategory === "cp" ? "text-pink-400" : "text-pink-500"}`} />
|
||||
<span className="text-[10px]">电竞CP</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleCategoryChange("coach")}
|
||||
className={`flex flex-col h-auto py-3 border-white/10 bg-white/5 hover:bg-white/10 hover:border-green-500/50 transition-all ${
|
||||
selectedCategory === "coach" ? "border-green-500 bg-green-500/20 ring-2 ring-green-500/50" : ""
|
||||
}`}
|
||||
>
|
||||
<Gamepad2
|
||||
className={`w-5 h-5 mb-1 ${selectedCategory === "coach" ? "text-green-400" : "text-green-500"}`}
|
||||
/>
|
||||
<span className="text-[10px]">游戏陪练</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="relative w-64 h-64">
|
||||
{(!matchResult || isMatching) && (
|
||||
<>
|
||||
@@ -198,12 +265,12 @@ export default function PlanetPage() {
|
||||
<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}`}
|
||||
src={matchResult.avatar || "/placeholder.svg"}
|
||||
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">
|
||||
<div className="absolute -bottom-1 left-1/2 -translate-x-1/2 bg-primary text-[10px] px-2 py-0.5 rounded-full text-white font-bold border-2 border-zinc-900">
|
||||
MATCHED
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,7 +289,7 @@ export default function PlanetPage() {
|
||||
|
||||
<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>
|
||||
<span className="text-xs font-bold text-green-400">正在自动跳转...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -243,12 +310,7 @@ export default function PlanetPage() {
|
||||
<>
|
||||
<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>
|
||||
<p className="text-xs text-white/70 mt-1">寻找{getCategoryLabel()}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -260,38 +322,18 @@ export default function PlanetPage() {
|
||||
<>
|
||||
<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="/gamer-avatar.jpg" alt="User" width={40} height={40} />
|
||||
<Image src="/user-avatar.jpg" alt="User" width={40} height={40} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
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">电竞CP</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
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>
|
||||
</Button>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-white/60">
|
||||
当前模式: <span className="text-primary font-bold">{getCategoryLabel()}</span>
|
||||
</p>
|
||||
<p className="text-xs text-white/40 mt-1">点击其他按钮可切换匹配类型</p>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
@@ -343,12 +385,7 @@ export default function PlanetPage() {
|
||||
key={avatar}
|
||||
className="w-6 h-6 rounded-full border border-black bg-gray-800 overflow-hidden"
|
||||
>
|
||||
<Image
|
||||
src={`/gamer-avatar.jpg?key=mup1i&height=24&width=24&query=gamer avatar ${avatar}`}
|
||||
alt="User"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Image src={`/avatar-${avatar}.jpg`} alt="User" width={24} height={24} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -64,12 +64,7 @@ export default function ProfilePage() {
|
||||
|
||||
<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"
|
||||
/>
|
||||
<Image src="/gamer-profile-avatar.jpg" 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>
|
||||
|
||||
93
app/recharge/page.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ArrowLeft, Wallet, CreditCard, Gem } from "lucide-react"
|
||||
import { useAppContext } from "@/components/providers/app-provider"
|
||||
|
||||
export default function RechargePage() {
|
||||
const router = useRouter()
|
||||
const { recharge } = useAppContext()
|
||||
|
||||
const amounts = [
|
||||
{ value: 6, bonus: 0 },
|
||||
{ value: 30, bonus: 2 },
|
||||
{ value: 68, bonus: 5 },
|
||||
{ value: 128, bonus: 12 },
|
||||
{ value: 328, bonus: 35 },
|
||||
{ value: 648, bonus: 80 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background pb-12">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70 hover:text-white">
|
||||
<ArrowLeft size={20} />
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">充值中心</h1>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-6">
|
||||
{/* Banner */}
|
||||
<div className="w-full h-32 rounded-2xl bg-gradient-to-r from-purple-600 to-blue-600 relative overflow-hidden flex items-center px-6">
|
||||
<div className="relative z-10">
|
||||
<h2 className="text-xl font-bold text-white mb-1">首充双倍</h2>
|
||||
<p className="text-xs text-white/80">限时特惠 赠送绝版头像框</p>
|
||||
</div>
|
||||
<div className="absolute right-0 bottom-0 w-32 h-32 opacity-50">
|
||||
<Gem className="w-full h-full text-white/20 rotate-12 translate-x-8 translate-y-8" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{amounts.map((item) => (
|
||||
<button
|
||||
key={item.value}
|
||||
className="glass-card p-4 rounded-xl flex flex-col items-center justify-center gap-2 hover:bg-white/10 transition-colors border border-white/5 hover:border-primary/50"
|
||||
onClick={() => recharge(item.value)}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<Gem size={16} className="text-primary" />
|
||||
<span className="font-bold text-lg">{item.value * 10}</span>
|
||||
</div>
|
||||
<span className="text-sm font-medium text-white/70">¥{item.value}</span>
|
||||
{item.bonus > 0 && (
|
||||
<span className="text-[10px] text-yellow-400 bg-yellow-500/10 px-1.5 rounded-full">
|
||||
送 {item.bonus * 10}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Payment Methods */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-bold text-sm text-white/70">支付方式</h3>
|
||||
<div className="glass-card rounded-xl p-0 overflow-hidden">
|
||||
<div className="flex items-center gap-3 p-4 border-b border-white/5">
|
||||
<div className="w-8 h-8 rounded-full bg-green-500/20 flex items-center justify-center">
|
||||
<Wallet size={18} className="text-green-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-bold text-sm">微信支付</div>
|
||||
<div className="text-[10px] text-white/40">推荐使用</div>
|
||||
</div>
|
||||
<div className="w-4 h-4 rounded-full border border-primary bg-primary" />
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-4">
|
||||
<div className="w-8 h-8 rounded-full bg-blue-500/20 flex items-center justify-center">
|
||||
<CreditCard size={18} className="text-blue-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-bold text-sm">支付宝</div>
|
||||
</div>
|
||||
<div className="w-4 h-4 rounded-full border border-white/20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-xs text-white/30 pt-4">充值即代表同意《用户充值协议》</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
3
app/services/account/loading.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
248
app/services/account/page.tsx
Normal file
@@ -0,0 +1,248 @@
|
||||
"use client"
|
||||
import { Search, Filter, Shield, Wallet, Gamepad2, ArrowUpRight } from "lucide-react"
|
||||
import { useRouter, useSearchParams } 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"
|
||||
import { AccountEvaluation } from "@/components/account/account-evaluation"
|
||||
|
||||
export default function AccountTradePage() {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const defaultTab = searchParams.get("tab") || "buy"
|
||||
|
||||
const games = [
|
||||
{ name: "全部", icon: null },
|
||||
{ name: "英雄联盟", icon: "/account-icon-lol.jpg" },
|
||||
{ name: "无畏契约", icon: "/account-icon-val.jpg" },
|
||||
{ name: "王者荣耀", icon: "/account-icon-hok.jpg" },
|
||||
{ name: "原神", icon: "/account-icon-gen.jpg" },
|
||||
]
|
||||
|
||||
const accounts = [
|
||||
{
|
||||
id: 1,
|
||||
title: "英雄联盟-全英雄 全皮肤 龙瞎",
|
||||
price: 2888,
|
||||
server: "艾欧尼亚",
|
||||
security: "找回包赔",
|
||||
image: "/account-lol.jpg",
|
||||
tags: ["顶号", "满级"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "无畏契约-满特工 冠军套",
|
||||
price: 1500,
|
||||
server: "国服",
|
||||
security: "保险护航",
|
||||
image: "/account-val.jpg",
|
||||
tags: ["成品号"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "王者荣耀-V10 300皮肤",
|
||||
price: 5600,
|
||||
server: "安卓QQ",
|
||||
security: "官方验号",
|
||||
image: "/account-hok.jpg",
|
||||
tags: ["贵族10", "国标"],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "原神-满命雷神 草神",
|
||||
price: 3200,
|
||||
server: "天空岛",
|
||||
security: "签署合同",
|
||||
image: "/account-genshin.jpg",
|
||||
tags: ["三神", "高练度"],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70 hover:text-white">
|
||||
返回
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">游戏账号交易</h1>
|
||||
<div className="ml-auto">{/* Placeholder for right action if needed */}</div>
|
||||
</header>
|
||||
|
||||
<Tabs defaultValue={defaultTab} className="flex flex-col min-h-[calc(100vh-60px)]">
|
||||
<div className="px-4 mt-2">
|
||||
<TabsList className="w-full bg-white/5 p-1 rounded-xl grid grid-cols-2">
|
||||
<TabsTrigger
|
||||
value="buy"
|
||||
className="text-sm font-bold data-[state=active]:bg-primary data-[state=active]:text-black"
|
||||
>
|
||||
账号买卖
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="finance"
|
||||
className="text-sm font-bold data-[state=active]:bg-purple-500 data-[state=active]:text-white"
|
||||
>
|
||||
账号金融
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<TabsContent value="buy" className="flex-1 px-4 space-y-4 mt-4 data-[state=inactive]:hidden">
|
||||
{/* Search & Filter */}
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-white/40" size={16} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索游戏、区服、英雄..."
|
||||
className="w-full bg-white/5 rounded-lg py-2.5 pl-9 pr-4 text-sm text-white focus:outline-none focus:ring-1 focus:ring-primary placeholder:text-white/30"
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" className="border-white/10 bg-white/5 text-white/70 w-10 px-0">
|
||||
<Filter size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Game Categories */}
|
||||
<div className="overflow-x-auto no-scrollbar pb-2">
|
||||
<div className="flex gap-3">
|
||||
{games.map((g, i) => (
|
||||
<div key={i} className="flex flex-col items-center gap-1 min-w-[60px] cursor-pointer group">
|
||||
<div
|
||||
className={`w-12 h-12 rounded-xl flex items-center justify-center border border-white/10 overflow-hidden transition-all group-hover:border-primary/50 ${!g.icon ? "bg-white/5" : "bg-black"}`}
|
||||
>
|
||||
{g.icon ? (
|
||||
<Image
|
||||
src={g.icon || "/placeholder.svg"}
|
||||
alt={g.name}
|
||||
width={48}
|
||||
height={48}
|
||||
className="object-cover"
|
||||
/>
|
||||
) : (
|
||||
<Gamepad2 className="text-white/50" />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] text-white/60 group-hover:text-white">{g.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Account List */}
|
||||
<div className="grid gap-3">
|
||||
{accounts.map((acc) => (
|
||||
<div
|
||||
key={acc.id}
|
||||
className="glass-card p-3 rounded-xl flex gap-3 hover:bg-white/5 transition-colors cursor-pointer group"
|
||||
onClick={() => toast({ title: "即将跳转详情", description: "该功能为模拟演示" })}
|
||||
>
|
||||
<div className="w-28 h-24 bg-muted rounded-lg relative overflow-hidden shrink-0 border border-white/5">
|
||||
<Image
|
||||
src={acc.image || "/placeholder.svg"}
|
||||
alt={acc.title}
|
||||
fill
|
||||
className="object-cover transition-transform group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute top-1 left-1 bg-black/60 text-[9px] px-1.5 py-0.5 rounded backdrop-blur-sm text-white/90 font-medium">
|
||||
{acc.server}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col justify-between py-0.5">
|
||||
<div>
|
||||
<h3 className="font-bold text-sm line-clamp-2 leading-snug">{acc.title}</h3>
|
||||
<div className="flex flex-wrap gap-1 mt-1.5">
|
||||
{acc.tags.map((tag) => (
|
||||
<span key={tag} className="text-[9px] bg-white/5 text-white/50 px-1 rounded">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end justify-between mt-auto">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-[10px] text-green-400 flex items-center gap-1 mb-0.5">
|
||||
<Shield size={10} /> {acc.security}
|
||||
</span>
|
||||
<span className="text-lg font-bold text-primary leading-none">¥{acc.price}</span>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-7 text-xs px-3 font-bold bg-white/10 hover:bg-primary hover:text-black"
|
||||
>
|
||||
购买
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="finance" className="flex-1 px-4 space-y-4 mt-4 data-[state=inactive]:hidden">
|
||||
{/* Finance Banner / Header Area */}
|
||||
<div className="bg-[#1A1A2E] p-6 rounded-3xl border border-purple-500/20 relative overflow-hidden shadow-lg">
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-purple-500/10 rounded-full blur-3xl -mr-10 -mt-10 pointer-events-none" />
|
||||
|
||||
<div className="flex items-center justify-between mb-6 relative z-10">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-purple-500/20 to-blue-500/20 flex items-center justify-center border border-white/10 shadow-inner">
|
||||
<Wallet size={24} className="text-purple-300" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-white">账号资产评估</h2>
|
||||
<p className="text-xs text-purple-200/60 mt-0.5">AI 智能估价 · 极速放款 · 银行级风控</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-xs text-white/40 mb-1">已评估账号</div>
|
||||
<div className="text-xl font-mono font-bold text-white tracking-tight">12,508</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 relative z-10">
|
||||
<div className="bg-[#12121A] rounded-xl p-3 text-center border border-white/5">
|
||||
<div className="text-[10px] text-white/40 mb-1.5 uppercase tracking-wider">放款总额</div>
|
||||
<div className="text-sm font-bold text-purple-300">¥ 1.2亿</div>
|
||||
</div>
|
||||
<div className="bg-[#12121A] rounded-xl p-3 text-center border border-white/5">
|
||||
<div className="text-[10px] text-white/40 mb-1.5 uppercase tracking-wider">平均额度</div>
|
||||
<div className="text-sm font-bold text-purple-300">¥ 3,500</div>
|
||||
</div>
|
||||
<div className="bg-[#12121A] rounded-xl p-3 text-center border border-white/5">
|
||||
<div className="text-[10px] text-white/40 mb-1.5 uppercase tracking-wider">最快到账</div>
|
||||
<div className="text-sm font-bold text-purple-300">5 分钟</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Evaluation Form Component */}
|
||||
<AccountEvaluation />
|
||||
|
||||
{/* Service Features */}
|
||||
<div className="grid grid-cols-2 gap-3 pb-8">
|
||||
<div className="bg-white/5 border border-white/5 p-3 rounded-xl flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-500/10 flex items-center justify-center">
|
||||
<Shield className="text-green-400" size={16} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs font-bold">安全保障</div>
|
||||
<div className="text-[10px] text-white/40">合同签署 法律生效</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white/5 border border-white/5 p-3 rounded-xl flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-yellow-500/10 flex items-center justify-center">
|
||||
<ArrowUpRight className="text-yellow-400" size={16} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs font-bold">极速放款</div>
|
||||
<div className="text-[10px] text-white/40">自动审核 秒级到账</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
93
app/services/leveling/page.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { Star, Zap, Trophy } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
|
||||
export default function LevelingPage() {
|
||||
const router = useRouter()
|
||||
|
||||
const boosters = [
|
||||
{ id: 1, title: "王者荣耀-荣耀王者直通车", price: 25, unit: "星", tags: ["极速", "金牌打手"], sales: 5000 },
|
||||
{ id: 2, title: "LOL-钻石大师晋级赛", price: 88, unit: "局", tags: ["包赢", "职业退役"], sales: 1200 },
|
||||
{ id: 3, title: "无畏契约-赋能战神代打", price: 50, unit: "胜场", tags: ["MVP", "直播"], sales: 800 },
|
||||
{ id: 4, title: "原神-深渊满星/神瞳全收集", price: 10, unit: "层", tags: ["纯手工", "安全"], sales: 3000 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-24 bg-background">
|
||||
<header className="sticky top-0 z-40 glass px-4 py-3 flex items-center gap-3">
|
||||
<button onClick={() => router.back()} className="text-white/70">
|
||||
返回
|
||||
</button>
|
||||
<h1 className="font-bold text-lg">游戏代练</h1>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Banner */}
|
||||
<div className="relative w-full h-32 rounded-xl overflow-hidden bg-gradient-to-r from-orange-500 to-red-600 flex items-center px-6">
|
||||
<div className="relative z-10">
|
||||
<h2 className="text-xl font-bold text-white mb-1">代练通严选</h2>
|
||||
<p className="text-xs text-white/80">官方认证打手 · 保证金赔付 · 效率保障</p>
|
||||
</div>
|
||||
<Trophy className="absolute right-4 bottom-[-10px] text-white/20 w-24 h-24 rotate-12" />
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex gap-2 overflow-x-auto no-scrollbar pb-2">
|
||||
{["全部", "王者荣耀", "英雄联盟", "无畏契约", "和平精英", "永劫无间"].map((tag, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`whitespace-nowrap px-4 py-1.5 rounded-full text-xs font-medium ${i === 0 ? "bg-primary text-black" : "bg-white/5 text-white/60"}`}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* List */}
|
||||
<div className="grid gap-3">
|
||||
{boosters.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="glass-card p-4 rounded-xl flex justify-between items-center hover:bg-white/10 transition-colors"
|
||||
onClick={() => toast({ title: "下单成功", description: "打手将在5分钟内上号" })}
|
||||
>
|
||||
<div>
|
||||
<h3 className="font-bold text-sm mb-1">{item.title}</h3>
|
||||
<div className="flex gap-2 mb-2">
|
||||
{item.tags.map((t, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="text-[10px] px-1.5 py-0.5 rounded bg-white/5 text-white/50 border border-white/5"
|
||||
>
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-xs text-white/40">
|
||||
<span className="flex items-center gap-1">
|
||||
<Zap size={12} className="text-yellow-500" /> 15分钟接单
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Star size={12} className="text-primary" /> 销量 {item.sales}+
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-lg font-bold text-primary">
|
||||
¥{item.price}
|
||||
<span className="text-xs text-white/50 font-normal">/{item.unit}</span>
|
||||
</div>
|
||||
<Button size="sm" className="h-7 text-xs mt-1">
|
||||
立即下单
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
91
app/zone/[id]/page.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { ArrowLeft, Users, Trophy, Sword, Flame } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export default function ZonePage({ params }: { params: { id: string } }) {
|
||||
const games: Record<string, any> = {
|
||||
lol: { name: "英雄联盟", color: "blue", bg: "bg-blue-900", image: "league of legends map summoners rift" },
|
||||
val: { name: "无畏契约", color: "red", bg: "bg-red-900", image: "valorant agent neon futuristic" },
|
||||
hok: { name: "王者荣耀", color: "yellow", bg: "bg-yellow-900", image: "honor of kings hero battle" },
|
||||
csgo: { name: "CS:GO", color: "orange", bg: "bg-orange-900", image: "csgo dust2 map tactical" },
|
||||
wow: { name: "魔兽世界", color: "yellow", bg: "bg-stone-900", image: "world of warcraft azeroth landscape" },
|
||||
}
|
||||
|
||||
const game = games[params.id] || games.lol
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen ${game.bg} text-white pb-24`}>
|
||||
{/* Header */}
|
||||
<div className="sticky top-0 z-40 p-4 flex items-center gap-4 bg-black/20 backdrop-blur-md">
|
||||
<Link href="/" className="p-2 bg-white/10 rounded-full hover:bg-white/20">
|
||||
<ArrowLeft size={20} />
|
||||
</Link>
|
||||
<h1 className="font-bold text-lg">{game.name}专区</h1>
|
||||
</div>
|
||||
|
||||
{/* Hero */}
|
||||
<div className="relative aspect-video w-full">
|
||||
<Image
|
||||
src={`/.jpg?key=zone-${params.id}&height=400&width=600&query=${game.image}`}
|
||||
alt={game.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-transparent" />
|
||||
<div className="absolute bottom-4 left-4">
|
||||
<h2 className="text-2xl font-bold mb-2">{game.name}</h2>
|
||||
<div className="flex gap-2">
|
||||
<span className="px-2 py-1 bg-white/10 rounded text-xs backdrop-blur-md">热度 1205万</span>
|
||||
<span className="px-2 py-1 bg-white/10 rounded text-xs backdrop-blur-md">开黑 3.2万车队</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="grid grid-cols-4 gap-2 p-4">
|
||||
<div className="bg-white/5 rounded-xl p-3 flex flex-col items-center gap-2">
|
||||
<Sword className="text-white/80" />
|
||||
<span className="text-xs">找车队</span>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-xl p-3 flex flex-col items-center gap-2">
|
||||
<Trophy className="text-white/80" />
|
||||
<span className="text-xs">打比赛</span>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-xl p-3 flex flex-col items-center gap-2">
|
||||
<Users className="text-white/80" />
|
||||
<span className="text-xs">找陪练</span>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-xl p-3 flex flex-col items-center gap-2">
|
||||
<Flame className="text-white/80" />
|
||||
<span className="text-xs">看直播</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="px-4 space-y-4">
|
||||
<h3 className="font-bold flex items-center gap-2">
|
||||
<div className={`w-1 h-4 bg-${game.color}-500 rounded-full`} />
|
||||
热门车队
|
||||
</h3>
|
||||
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="bg-white/5 rounded-xl p-4 flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-full bg-white/10 relative overflow-hidden">
|
||||
<Image
|
||||
src={`/.jpg?key=team-${i}&height=100&width=100&query=esports avatar ${game.name}`}
|
||||
alt="Avatar"
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-sm">钻石冲分车队 缺1</h4>
|
||||
<p className="text-xs text-white/50">胜率队 | 只要狠人 | 连胜中</p>
|
||||
</div>
|
||||
<button className={`px-4 py-1.5 rounded-full bg-${game.color}-600 text-xs font-bold`}>申请</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
329
components/account/account-evaluation.tsx
Normal file
@@ -0,0 +1,329 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
|
||||
import { useState } from "react"
|
||||
import { motion, AnimatePresence } from "framer-motion"
|
||||
import { Check, Info, Calculator, RefreshCw, Shield, Wallet, Upload, ImageIcon, X } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { GAME_CASCADE_DATA } from "@/lib/data/game-cascade-data"
|
||||
import { DynamicCascadeForm } from "./dynamic-cascade-form"
|
||||
import { evaluateAccount, type EvaluationResult } from "@/lib/utils/account-evaluation"
|
||||
|
||||
export function AccountEvaluation() {
|
||||
const [step, setStep] = useState(1)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [result, setResult] = useState<EvaluationResult | null>(null)
|
||||
const [showQRCode, setShowQRCode] = useState(false)
|
||||
|
||||
const [game, setGame] = useState("王者荣耀")
|
||||
const [cascadeData, setCascadeData] = useState<Record<string, any>>({})
|
||||
|
||||
const [phoneNumber, setPhoneNumber] = useState("")
|
||||
|
||||
const [screenshots, setScreenshots] = useState<File[]>([])
|
||||
|
||||
const handleScreenshotUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = e.target.files
|
||||
if (files) {
|
||||
const newFiles = Array.from(files).slice(0, 5 - screenshots.length)
|
||||
setScreenshots([...screenshots, ...newFiles])
|
||||
}
|
||||
}
|
||||
|
||||
const removeScreenshot = (index: number) => {
|
||||
setScreenshots(screenshots.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const handleEvaluate = () => {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
const res = evaluateAccount({
|
||||
gameName: game,
|
||||
serverType: "热门服",
|
||||
currentLevel: 30,
|
||||
maxLevel: 30,
|
||||
rankName: "荣耀王者",
|
||||
assetCounts: {
|
||||
legendary: 0,
|
||||
epic: 0,
|
||||
rare: 0,
|
||||
common: 0,
|
||||
},
|
||||
registrationMonths: 12,
|
||||
rechargeAmount6Months: 0,
|
||||
mallConsumption: 0,
|
||||
isRealNameVerified: true,
|
||||
isRealNameOwner: true,
|
||||
hasBanRecord: false,
|
||||
})
|
||||
|
||||
setResult(res)
|
||||
setLoading(false)
|
||||
setStep(2)
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
setResult(null)
|
||||
setStep(1)
|
||||
}
|
||||
|
||||
const availableGames = Object.keys(GAME_CASCADE_DATA)
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<AnimatePresence mode="wait">
|
||||
{step === 1 ? (
|
||||
<motion.div
|
||||
key="form"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="space-y-6 bg-[#1A1A2E] border border-white/5 rounded-3xl p-6 shadow-xl relative overflow-hidden"
|
||||
>
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-blue-600/10 rounded-full blur-3xl -mr-10 -mt-10 pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-32 h-32 bg-purple-600/10 rounded-full blur-3xl -ml-10 -mb-10 pointer-events-none" />
|
||||
|
||||
<div className="space-y-5 relative z-10">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">选择游戏</Label>
|
||||
<Select value={game} onValueChange={setGame}>
|
||||
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white max-h-[300px]">
|
||||
{availableGames.map((g) => (
|
||||
<SelectItem key={g} value={g}>
|
||||
{g}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<DynamicCascadeForm gameName={game} onDataChange={setCascadeData} />
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-gray-400 flex items-center gap-2">
|
||||
<ImageIcon size={16} />
|
||||
上传游戏截图 (最多5张)
|
||||
</Label>
|
||||
<div className="border-2 border-dashed border-white/10 rounded-xl p-4 bg-[#12121A] hover:border-blue-500/30 transition-colors">
|
||||
<input
|
||||
type="file"
|
||||
id="screenshot-upload"
|
||||
multiple
|
||||
accept="image/*"
|
||||
onChange={handleScreenshotUpload}
|
||||
className="hidden"
|
||||
disabled={screenshots.length >= 5}
|
||||
/>
|
||||
<label
|
||||
htmlFor="screenshot-upload"
|
||||
className={`flex flex-col items-center justify-center gap-2 cursor-pointer ${
|
||||
screenshots.length >= 5 ? "opacity-50 cursor-not-allowed" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="w-12 h-12 rounded-full bg-blue-500/10 flex items-center justify-center">
|
||||
<Upload className="text-blue-400" size={24} />
|
||||
</div>
|
||||
<p className="text-sm text-gray-400">
|
||||
{screenshots.length >= 5 ? "已达上传上限" : "点击上传账号截图"}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600">支持 JPG、PNG 格式</p>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{screenshots.length > 0 && (
|
||||
<div className="grid grid-cols-3 gap-3 mt-3">
|
||||
{screenshots.map((file, index) => (
|
||||
<div key={index} className="relative group">
|
||||
<div className="aspect-square rounded-lg overflow-hidden bg-[#12121A] border border-white/10">
|
||||
<img
|
||||
src={URL.createObjectURL(file) || "/placeholder.svg"}
|
||||
alt={`Screenshot ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => removeScreenshot(index)}
|
||||
className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<X size={14} className="text-white" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">联系电话</Label>
|
||||
<Input
|
||||
type="tel"
|
||||
value={phoneNumber}
|
||||
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||
placeholder="请输入您的手机号码"
|
||||
className="bg-[#12121A] border-white/10 h-11 text-white focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="w-full h-14 text-lg font-bold bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-500 hover:to-purple-500 shadow-lg shadow-blue-500/20 rounded-xl mt-4"
|
||||
onClick={handleEvaluate}
|
||||
disabled={loading || !phoneNumber}
|
||||
>
|
||||
{loading ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<RefreshCw className="animate-spin" size={20} />
|
||||
正在智能估值...
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Calculator size={20} />
|
||||
开始免费评估
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<p className="text-center text-xs text-white/30 flex items-center justify-center gap-1.5 mt-4">
|
||||
<Shield size={12} />
|
||||
评估过程完全匿名,不会记录您的账号密码
|
||||
</p>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="result"
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="bg-[#1A1A2E] border border-white/10 rounded-3xl overflow-hidden shadow-2xl"
|
||||
>
|
||||
<div className="bg-gradient-to-br from-[#1A1A2E] to-black p-6 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-blue-600/10 rounded-full blur-3xl -mr-16 -mt-16 pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-64 h-64 bg-purple-600/10 rounded-full blur-3xl -ml-16 -mb-16 pointer-events-none" />
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div>
|
||||
<h2 className="text-gray-400 text-sm mb-1.5">账号初步估值</h2>
|
||||
<div className="text-5xl font-bold text-white flex items-end gap-3 tracking-tight">
|
||||
¥ {result?.valuation.toLocaleString()}
|
||||
<span className="text-sm text-gray-500 font-medium mb-2">CNY</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="inline-flex items-center gap-1.5 bg-[#0A2F1F] text-green-400 px-4 py-1.5 rounded-full text-xs font-bold border border-green-500/20">
|
||||
<Check size={12} />
|
||||
风控通过
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-r from-yellow-900/20 to-orange-900/20 border border-yellow-500/20 rounded-2xl p-5 mb-8 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-yellow-500/5" />
|
||||
<div className="relative z-10">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<div className="text-yellow-100 font-bold flex items-center gap-2 text-lg">
|
||||
<Wallet size={20} className="text-yellow-500" />
|
||||
可贷额度
|
||||
{result?.details.consumptionMultiplier && result.details.consumptionMultiplier > 1 && (
|
||||
<span className="text-[10px] px-2 py-0.5 bg-red-500/20 text-red-300 rounded-full border border-red-500/30">
|
||||
已提额 +{((result.details.consumptionMultiplier - 1) * 100).toFixed(0)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-3xl font-bold text-yellow-400">¥ {result?.loanAmount.toLocaleString()}</div>
|
||||
</div>
|
||||
<div className="w-full bg-black/40 h-2 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="bg-yellow-500 h-full rounded-full shadow-[0_0_10px_rgba(234,179,8,0.5)]"
|
||||
style={{ width: "60%" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-yellow-200/50 mt-3 flex items-center gap-1.5">
|
||||
<Info size={12} />
|
||||
额度为估值的 60%,最快 5 分钟放款
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||
<div className="bg-[#12121A] p-4 rounded-xl border border-white/5">
|
||||
<div className="text-xs text-gray-500 mb-1.5">基础价值分</div>
|
||||
<div className="text-xl font-bold text-white">
|
||||
{result?.details.baseScore} <span className="text-xs text-gray-600 font-normal">/ 50</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-[#12121A] p-4 rounded-xl border border-white/5">
|
||||
<div className="text-xs text-gray-500 mb-1.5">核心资产分</div>
|
||||
<div className="text-xl font-bold text-white">
|
||||
{result?.details.assetScore} <span className="text-xs text-gray-600 font-normal">/ 30</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-[#12121A] p-4 rounded-xl border border-white/5">
|
||||
<div className="text-xs text-gray-500 mb-1.5">辅助加分</div>
|
||||
<div className="text-xl font-bold text-white">
|
||||
{result?.details.bonusScore} <span className="text-xs text-gray-600 font-normal">/ 20</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-[#12121A] p-4 rounded-xl border border-white/5">
|
||||
<div className="text-xs text-gray-500 mb-1.5">风控系数</div>
|
||||
<div
|
||||
className={`text-xl font-bold ${result?.details.riskCoefficient === 1.2 ? "text-green-400" : "text-yellow-400"}`}
|
||||
>
|
||||
×{result?.details.riskCoefficient}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1 bg-transparent border-white/10 text-white hover:bg-white/5 h-12 rounded-xl"
|
||||
onClick={reset}
|
||||
>
|
||||
重新评估
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1 bg-yellow-500 hover:bg-yellow-400 text-black font-bold h-12 rounded-xl text-base shadow-[0_0_20px_rgba(234,179,8,0.2)]"
|
||||
onClick={() => setShowQRCode(true)}
|
||||
>
|
||||
立即变现
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<Dialog open={showQRCode} onOpenChange={setShowQRCode}>
|
||||
<DialogContent className="bg-[#1A1A2E] border-white/10 text-white max-w-sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-center text-xl font-bold">联系客服完成变现</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col items-center gap-4 py-6">
|
||||
<div className="w-48 h-48 bg-white rounded-xl p-4 flex items-center justify-center">
|
||||
<div className="text-center text-black text-xs">
|
||||
<div className="w-40 h-40 bg-gradient-to-br from-green-400 to-green-600 rounded-lg flex items-center justify-center mb-2">
|
||||
<span className="text-white text-4xl font-bold">微信</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center space-y-2">
|
||||
<p className="text-gray-400 text-sm">扫描二维码添加客服微信</p>
|
||||
<p className="text-white font-bold">预计放款时间: 5-10分钟</p>
|
||||
<p className="text-xs text-gray-500">客服工作时间: 9:00 - 22:00</p>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
204
components/account/dynamic-cascade-form.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { GAME_CASCADE_DATA } from "@/lib/data/game-cascade-data"
|
||||
|
||||
type DynamicCascadeFormProps = {
|
||||
gameName: string
|
||||
onDataChange: (data: Record<string, any>) => void
|
||||
}
|
||||
|
||||
export function DynamicCascadeForm({ gameName, onDataChange }: DynamicCascadeFormProps) {
|
||||
const config = GAME_CASCADE_DATA[gameName]
|
||||
|
||||
const [selectedRegion, setSelectedRegion] = useState<string>("")
|
||||
const [selectedServer, setSelectedServer] = useState<string>("")
|
||||
const [selectedSubOption, setSelectedSubOption] = useState<string>("")
|
||||
const [attributeValues, setAttributeValues] = useState<Record<string, any>>({})
|
||||
|
||||
useEffect(() => {
|
||||
// Reset when game changes
|
||||
setSelectedRegion("")
|
||||
setSelectedServer("")
|
||||
setSelectedSubOption("")
|
||||
setAttributeValues({})
|
||||
}, [gameName])
|
||||
|
||||
useEffect(() => {
|
||||
// Notify parent of data changes
|
||||
onDataChange({
|
||||
region: selectedRegion,
|
||||
server: selectedServer,
|
||||
subOption: selectedSubOption,
|
||||
...attributeValues,
|
||||
})
|
||||
}, [selectedRegion, selectedServer, selectedSubOption, attributeValues, onDataChange])
|
||||
|
||||
if (!config) {
|
||||
return <div className="text-gray-400 text-sm">该游戏暂无配置</div>
|
||||
}
|
||||
|
||||
const currentRegion = config.regions.find((r) => r.value === selectedRegion)
|
||||
const serverOptions = currentRegion?.children || []
|
||||
|
||||
const currentServer = serverOptions.find((s) => s.value === selectedServer)
|
||||
const subOptions = currentServer?.children || []
|
||||
|
||||
const handleAttributeChange = (label: string, value: any) => {
|
||||
setAttributeValues((prev) => ({ ...prev, [label]: value }))
|
||||
}
|
||||
|
||||
const handleMultiSelectChange = (label: string, option: string, checked: boolean) => {
|
||||
setAttributeValues((prev) => {
|
||||
const current = prev[label] || []
|
||||
if (checked) {
|
||||
return { ...prev, [label]: [...current, option] }
|
||||
} else {
|
||||
return { ...prev, [label]: current.filter((v: string) => v !== option) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
{/* Region Selection */}
|
||||
{config.regions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">游戏区</Label>
|
||||
<Select
|
||||
value={selectedRegion}
|
||||
onValueChange={(v) => {
|
||||
setSelectedRegion(v)
|
||||
setSelectedServer("")
|
||||
setSelectedSubOption("")
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
|
||||
<SelectValue placeholder="选择游戏区" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white">
|
||||
{config.regions.map((region) => (
|
||||
<SelectItem key={region.value} value={region.value}>
|
||||
{region.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Server Selection */}
|
||||
{serverOptions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">游戏服</Label>
|
||||
<Select
|
||||
value={selectedServer}
|
||||
onValueChange={(v) => {
|
||||
setSelectedServer(v)
|
||||
setSelectedSubOption("")
|
||||
}}
|
||||
disabled={!selectedRegion}
|
||||
>
|
||||
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
|
||||
<SelectValue placeholder="选择服务器" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white max-h-[300px]">
|
||||
{serverOptions.map((server) => (
|
||||
<SelectItem key={server.value} value={server.value}>
|
||||
{server.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sub Options */}
|
||||
{subOptions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">游戏属性</Label>
|
||||
<Select value={selectedSubOption} onValueChange={setSelectedSubOption} disabled={!selectedServer}>
|
||||
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
|
||||
<SelectValue placeholder="选择属性" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white">
|
||||
{subOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dynamic Attributes */}
|
||||
{config.attributes.map((attr, index) => (
|
||||
<div key={index} className="space-y-2">
|
||||
<Label className="text-gray-400">
|
||||
{attr.label}
|
||||
{attr.required && <span className="text-red-400 ml-1">*</span>}
|
||||
</Label>
|
||||
|
||||
{attr.type === "select" && attr.options && (
|
||||
<Select
|
||||
value={attributeValues[attr.label] || ""}
|
||||
onValueChange={(v) => handleAttributeChange(attr.label, v)}
|
||||
>
|
||||
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-purple-500">
|
||||
<SelectValue placeholder={`选择${attr.label}`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white max-h-[300px]">
|
||||
{attr.options.map((option) => (
|
||||
<SelectItem key={option} value={option}>
|
||||
{option}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
|
||||
{attr.type === "multiselect" && attr.options && (
|
||||
<div className="bg-[#12121A] border border-white/10 rounded-lg p-4 max-h-[200px] overflow-y-auto space-y-2">
|
||||
{attr.options.slice(0, 20).map((option) => (
|
||||
<div key={option} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`${attr.label}-${option}`}
|
||||
checked={(attributeValues[attr.label] || []).includes(option)}
|
||||
onCheckedChange={(checked) => handleMultiSelectChange(attr.label, option, checked as boolean)}
|
||||
className="border-white/20"
|
||||
/>
|
||||
<label htmlFor={`${attr.label}-${option}`} className="text-sm text-gray-300 cursor-pointer">
|
||||
{option}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
{attr.options.length > 20 && <div className="text-xs text-gray-500 text-center pt-2">显示前20项...</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{attr.type === "input" && (
|
||||
<Input
|
||||
value={attributeValues[attr.label] || ""}
|
||||
onChange={(e) => handleAttributeChange(attr.label, e.target.value)}
|
||||
placeholder={`输入${attr.label}`}
|
||||
className="bg-[#12121A] border-white/10 h-11 text-white focus:border-purple-500"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Screenshot Upload Hint */}
|
||||
<div className="bg-[#12121A] border border-blue-500/20 rounded-lg p-3 mt-4">
|
||||
<p className="text-xs text-blue-300 flex items-center gap-2">
|
||||
<span className="text-blue-500">📸</span>
|
||||
请准备游戏截图以便进行账号验证
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
86
components/home/star-player-section.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
"use client"
|
||||
|
||||
const PLAYERS = [
|
||||
{
|
||||
id: "1",
|
||||
name: "GM-远洋",
|
||||
title: "国服第一盲僧",
|
||||
price: "50币/局",
|
||||
image: "/avatar-1.jpg",
|
||||
isLive: true,
|
||||
viewers: "12.5w",
|
||||
orders: 1205,
|
||||
fans: "4.6w",
|
||||
rank: "最强王者",
|
||||
intro: "前职业选手,S9国服第一盲僧,擅长打野节奏带动,包教包会。人皮话多,心态极好,带你体验飞一般的感觉!",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "卡若",
|
||||
title: "职业战队退役",
|
||||
price: "80币/局",
|
||||
image: "/avatar-2.jpg",
|
||||
isLive: true,
|
||||
viewers: "8.2w",
|
||||
orders: 890,
|
||||
fans: "3.2w",
|
||||
rank: "峡谷之巅 800点",
|
||||
intro: "退役职业选手,意识超群,指挥型打野。想学运营和意识的来,带你躺赢。",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "小柠檬",
|
||||
title: "人皮话多",
|
||||
price: "30币/局",
|
||||
image: "/avatar-3.jpg",
|
||||
isLive: false,
|
||||
viewers: "0",
|
||||
orders: 3400,
|
||||
fans: "15.8w",
|
||||
rank: "超凡大师",
|
||||
intro: "声音甜美,心态好,不压力队友。主玩软辅,保护型辅助,给你最贴心的守护。",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "阿伟",
|
||||
title: "绝地求生刚枪王",
|
||||
price: "60币/局",
|
||||
image: "/avatar-4.jpg",
|
||||
isLive: false,
|
||||
viewers: "0",
|
||||
orders: 560,
|
||||
fans: "1.2w",
|
||||
rank: "亚服前500",
|
||||
intro: "亚服路人王,刚枪猛男,带你吃鸡带你飞。车技一流,意识无敌。",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "七七",
|
||||
title: "甜美声优",
|
||||
price: "40币/局",
|
||||
image: "/avatar-5.jpg",
|
||||
isLive: true,
|
||||
viewers: "5.6w",
|
||||
orders: 2100,
|
||||
fans: "8.9w",
|
||||
rank: "钻石I",
|
||||
intro: "全能补位,声优出道。不仅游戏打得好,唱歌还好听,快来点我吧!",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "大魔王",
|
||||
title: "中单法王",
|
||||
price: "100币/局",
|
||||
image: "/avatar-6.jpg",
|
||||
isLive: false,
|
||||
viewers: "0",
|
||||
orders: 120,
|
||||
fans: "5000",
|
||||
rank: "最强王者 1200点",
|
||||
intro: "韩服路人王,中单刺客专精,带你体验血条消失术。",
|
||||
},
|
||||
]
|
||||
|
||||
export function StarPlayerSection() {
|
||||
return null
|
||||
}
|
||||
32
components/ui/checkbox.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
|
||||
import { CheckIcon } from 'lucide-react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
data-slot="checkbox-indicator"
|
||||
className="flex items-center justify-center text-current transition-none"
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox }
|
||||
24
components/ui/label.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as LabelPrimitive from '@radix-ui/react-label'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
function Label({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
return (
|
||||
<LabelPrimitive.Root
|
||||
data-slot="label"
|
||||
className={cn(
|
||||
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Label }
|
||||
185
components/ui/select.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as SelectPrimitive from '@radix-ui/react-select'
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
function Select({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||
return <SelectPrimitive.Root data-slot="select" {...props} />
|
||||
}
|
||||
|
||||
function SelectGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
||||
}
|
||||
|
||||
function SelectValue({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
||||
}
|
||||
|
||||
function SelectTrigger({
|
||||
className,
|
||||
size = 'default',
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||
size?: 'sm' | 'default'
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.Trigger
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]: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 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDownIcon className="size-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectContent({
|
||||
className,
|
||||
children,
|
||||
position = 'popper',
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||
return (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className,
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
||||
return (
|
||||
<SelectPrimitive.Label
|
||||
data-slot="select-label"
|
||||
className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
||||
return (
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
||||
return (
|
||||
<SelectPrimitive.Separator
|
||||
data-slot="select-separator"
|
||||
className={cn('bg-border pointer-events-none -mx-1 my-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollUpButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollDownButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
}
|
||||
540
lib/data/game-cascade-data.ts
Normal file
@@ -0,0 +1,540 @@
|
||||
export type CascadeLevel = {
|
||||
value: string
|
||||
label: string
|
||||
children?: CascadeLevel[]
|
||||
}
|
||||
|
||||
export type GameCascadeConfig = {
|
||||
productType: string // 商品类型
|
||||
regions: CascadeLevel[] // 游戏区
|
||||
attributes: {
|
||||
label: string
|
||||
type: "select" | "multiselect" | "input" | "slider"
|
||||
options?: string[]
|
||||
required?: boolean
|
||||
}[]
|
||||
}
|
||||
|
||||
export const GAME_CASCADE_DATA: Record<string, GameCascadeConfig> = {
|
||||
三角洲行动: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "苹果",
|
||||
label: "苹果",
|
||||
children: [
|
||||
{
|
||||
value: "全区全服",
|
||||
label: "全区全服",
|
||||
children: [
|
||||
{ value: "可二次", label: "可二次实名" },
|
||||
{ value: "不可二次", label: "不可二次实名" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "安卓",
|
||||
label: "安卓",
|
||||
children: [
|
||||
{
|
||||
value: "全区全服",
|
||||
label: "全区全服",
|
||||
children: [
|
||||
{ value: "可二次", label: "可二次实名" },
|
||||
{ value: "不可二次", label: "不可二次实名" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "steam",
|
||||
label: "steam",
|
||||
children: [{ value: "全区全服", label: "全区全服" }],
|
||||
},
|
||||
],
|
||||
attributes: [],
|
||||
},
|
||||
|
||||
梦幻西游畅玩服: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "畅玩服",
|
||||
label: "畅玩服",
|
||||
children: [
|
||||
{ value: "建邺城", label: "建邺城" },
|
||||
{ value: "龙拳", label: "龙拳" },
|
||||
{ value: "横扫千军", label: "横扫千军" },
|
||||
{ value: "龙卷雨击", label: "龙卷雨击" },
|
||||
{ value: "纵横天下", label: "纵横天下" },
|
||||
{ value: "紫气东来", label: "紫气东来" },
|
||||
{ value: "无与伦比", label: "无与伦比" },
|
||||
{ value: "紫禁之巅", label: "紫禁之巅" },
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "门派",
|
||||
type: "select",
|
||||
options: [
|
||||
"大唐官府",
|
||||
"龙宫",
|
||||
"化生寺",
|
||||
"盘丝洞",
|
||||
"普陀山",
|
||||
"天宫",
|
||||
"女儿村",
|
||||
"五庄观",
|
||||
"方寸山",
|
||||
"魔王寨",
|
||||
"阴曹地府",
|
||||
"狮驼岭",
|
||||
"无",
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "角色性别",
|
||||
type: "select",
|
||||
options: ["男", "女"],
|
||||
},
|
||||
{
|
||||
label: "尊享权益",
|
||||
type: "select",
|
||||
options: ["超级绿通(2000元)", "高级绿通(540元)", "中级绿通(90元)", "无"],
|
||||
},
|
||||
{
|
||||
label: "整组号",
|
||||
type: "select",
|
||||
options: ["是", "否"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
坦克世界: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "360国服",
|
||||
label: "360国服",
|
||||
},
|
||||
{
|
||||
value: "其他服",
|
||||
label: "其他服",
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "有无违规插件",
|
||||
type: "select",
|
||||
options: ["无", "有", "未查询未知"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "十级高级车辆",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"无",
|
||||
"VK7201",
|
||||
"07P(E)主战方案",
|
||||
"WT12",
|
||||
"777工程机动型",
|
||||
"260工程",
|
||||
"780工程",
|
||||
"T-22",
|
||||
"907工程",
|
||||
"268工程V型",
|
||||
"T95E6",
|
||||
"M60",
|
||||
"113BO",
|
||||
"WZ111QL",
|
||||
"齐天大圣",
|
||||
"121B",
|
||||
"T95酋长",
|
||||
"酋长MK.VI",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "九级高级车辆",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"无",
|
||||
"50吨主战方案",
|
||||
"昆泽试验车",
|
||||
"T-55A",
|
||||
"07HK主战方案3型",
|
||||
"777工程II型",
|
||||
"283工程",
|
||||
"K-91II型",
|
||||
"AE一期",
|
||||
"1B概念车",
|
||||
"WZ114",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "八级高级车辆",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"无",
|
||||
"59式",
|
||||
"黄金59式",
|
||||
"BZ-176",
|
||||
"703工程II型",
|
||||
"E-75TS",
|
||||
"IS-3A",
|
||||
"SU-130PM",
|
||||
"T-44-100",
|
||||
"超级潘兴",
|
||||
"天蝎",
|
||||
"克莱斯勒K",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
穿越火线: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "电信",
|
||||
label: "电信",
|
||||
children: [
|
||||
{ value: "安徽一区", label: "安徽一区" },
|
||||
{ value: "福建一区", label: "福建一区" },
|
||||
{ value: "广东一区", label: "广东一区" },
|
||||
{ value: "广东二区", label: "广东二区" },
|
||||
{ value: "湖北一区", label: "湖北一区" },
|
||||
{ value: "江苏一区", label: "江苏一区" },
|
||||
{ value: "上海一区", label: "上海一区" },
|
||||
{ value: "四川一区", label: "四川一区" },
|
||||
{ value: "浙江一区", label: "浙江一区" },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "网通",
|
||||
label: "网通",
|
||||
children: [
|
||||
{ value: "北京一区", label: "北京一区" },
|
||||
{ value: "北京二区", label: "北京二区" },
|
||||
{ value: "河南一区", label: "河南一区" },
|
||||
{ value: "辽宁一区", label: "辽宁一区" },
|
||||
{ value: "山东一区", label: "山东一区" },
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "英雄武器",
|
||||
type: "select",
|
||||
options: ["无英雄武器", "1-5V", "6-10V", "11-20V", "20-30V", "30V以上"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "所属战区",
|
||||
type: "select",
|
||||
options: [
|
||||
"华北战区",
|
||||
"河南战区",
|
||||
"山东战区",
|
||||
"东北战区",
|
||||
"华中战区",
|
||||
"华南战区",
|
||||
"西部战区",
|
||||
"东部战区",
|
||||
"江苏战区",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "排位级别",
|
||||
type: "select",
|
||||
options: ["无排位", "新锐", "精英", "专家", "大师", "宗师", "枪王", "枪王之王"],
|
||||
},
|
||||
{
|
||||
label: "实名修改",
|
||||
type: "select",
|
||||
options: ["可修改实名", "不可修改实名"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
王者荣耀: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "苹果",
|
||||
label: "苹果",
|
||||
children: [
|
||||
{ value: "苹果QQ", label: "苹果QQ" },
|
||||
{ value: "苹果微信", label: "苹果微信" },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "安卓",
|
||||
label: "安卓",
|
||||
children: [
|
||||
{ value: "安卓QQ", label: "安卓QQ" },
|
||||
{ value: "安卓微信", label: "安卓微信" },
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "防沉迷",
|
||||
type: "select",
|
||||
options: ["无防沉迷", "有防沉迷"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "实名修改",
|
||||
type: "select",
|
||||
options: ["可修改", "不可修改"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "荣耀水晶皮肤",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"倪克斯神谕-武则天",
|
||||
"九霄神辉-花木兰",
|
||||
"大秦宣太后-芈月",
|
||||
"鸣剑曳影-李白",
|
||||
"幻阙歌-貂蝉",
|
||||
"星空梦想-鲁班七号",
|
||||
"赤影疾锋-关羽",
|
||||
"天鹅之梦-小乔",
|
||||
"全息碎影-孙悟空",
|
||||
"银白咏叹调-铠",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "传说皮肤",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"音你心动-小乔",
|
||||
"淬星耀世-赵云",
|
||||
"末日机甲-孙尚香",
|
||||
"天魔缭乱-吕布",
|
||||
"凤求凰-李白",
|
||||
"武圣-关羽",
|
||||
"仲夏夜之梦-貂蝉",
|
||||
"地狱火-孙悟空",
|
||||
"齐天大圣-孙悟空",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
和平精英: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "安卓",
|
||||
label: "安卓",
|
||||
children: [{ value: "QQ", label: "QQ" }],
|
||||
},
|
||||
{
|
||||
value: "苹果",
|
||||
label: "苹果",
|
||||
children: [{ value: "微信", label: "微信" }],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "热门载具",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"阿斯顿·马丁DBS(红)",
|
||||
"阿斯顿·马丁DBX(橙)",
|
||||
"鬼马风驰",
|
||||
"海龙奔腾",
|
||||
"金龙耀世",
|
||||
"兰博基尼URUS(炫光金)",
|
||||
"玛莎拉蒂MC20(幻星粉)",
|
||||
"迈凯伦570S(炫冰蓝)",
|
||||
"特斯拉Cybertruck(镭光铂)",
|
||||
"焰龙咆哮",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "热门枪械",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"AKM百合蜜语",
|
||||
"AKM-盖世英雄",
|
||||
"AWM花间之火",
|
||||
"AWM焰龙焚天",
|
||||
"GROZA焦糖拿铁",
|
||||
"Kar98K圣光天使",
|
||||
"M416风花雪月",
|
||||
"M416五爪金龙",
|
||||
"SCAR-L-纯梦嫁纱",
|
||||
"UMP45相思残雪",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
原神: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "mihoyo服",
|
||||
label: "mihoyo服",
|
||||
children: [{ value: "天空服", label: "天空服" }],
|
||||
},
|
||||
{
|
||||
value: "B服",
|
||||
label: "B服",
|
||||
children: [{ value: "B服", label: "B服" }],
|
||||
},
|
||||
{
|
||||
value: "国际服",
|
||||
label: "国际服",
|
||||
children: [{ value: "国际服", label: "国际服" }],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "邮箱",
|
||||
type: "select",
|
||||
options: ["未绑定邮箱", "网易邮箱出售", "QQ邮箱", "字母Q邮箱", "其他邮箱", "邮箱不出售"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "邮箱实名",
|
||||
type: "select",
|
||||
options: ["已实名", "未实名"],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "五星角色",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"林尼",
|
||||
"艾尔海森",
|
||||
"赛诺",
|
||||
"妮露",
|
||||
"纳西妲",
|
||||
"流浪者",
|
||||
"琴",
|
||||
"迪卢克",
|
||||
"七七",
|
||||
"莫娜",
|
||||
"刻晴",
|
||||
"钟离",
|
||||
"甘雨",
|
||||
"胡桃",
|
||||
"雷电将军",
|
||||
"枫原万叶",
|
||||
"神里绫华",
|
||||
"那维莱特",
|
||||
"芙宁娜",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "五星武器",
|
||||
type: "multiselect",
|
||||
options: [
|
||||
"磐岩结绿",
|
||||
"猎人之径",
|
||||
"圣显之匙",
|
||||
"千夜浮梦",
|
||||
"风鹰剑",
|
||||
"天空之刃",
|
||||
"狼的末路",
|
||||
"护摩之杖",
|
||||
"雾切之回光",
|
||||
"薙草之稻光",
|
||||
"苍古自由之誓",
|
||||
"阿莫斯之弓",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
穿越火线手游: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "安卓",
|
||||
label: "安卓",
|
||||
children: [
|
||||
{ value: "安卓QQ", label: "安卓QQ" },
|
||||
{ value: "安卓微信", label: "安卓微信" },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "苹果",
|
||||
label: "苹果",
|
||||
children: [
|
||||
{ value: "苹果QQ", label: "苹果QQ" },
|
||||
{ value: "苹果微信", label: "苹果微信" },
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
{
|
||||
label: "客户端",
|
||||
type: "select",
|
||||
options: ["全部", "苹果微信", "苹果QQ", "安卓微信", "安卓QQ", "全服通用"],
|
||||
},
|
||||
{
|
||||
label: "实名修改",
|
||||
type: "select",
|
||||
options: ["可修改", "不可修改"],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
魔兽世界: {
|
||||
productType: "账号",
|
||||
regions: [
|
||||
{
|
||||
value: "亚服",
|
||||
label: "亚服",
|
||||
children: [
|
||||
{
|
||||
value: "大地的裂变",
|
||||
label: "大地的裂变",
|
||||
children: [
|
||||
{ value: "伊弗斯", label: "伊弗斯" },
|
||||
{ value: "瑪拉頓", label: "瑪拉頓" },
|
||||
{ value: "阿拉希盆地", label: "阿拉希盆地" },
|
||||
{ value: "鱼人", label: "鱼人" },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "正式服",
|
||||
label: "正式服",
|
||||
children: [
|
||||
{ value: "暗影之月", label: "暗影之月" },
|
||||
{ value: "众星之子", label: "众星之子" },
|
||||
{ value: "地狱吼", label: "地狱吼" },
|
||||
{ value: "世界之树", label: "世界之树" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "国服",
|
||||
label: "国服",
|
||||
children: [
|
||||
{
|
||||
value: "正式服",
|
||||
label: "正式服",
|
||||
children: [
|
||||
{ value: "回音山", label: "回音山" },
|
||||
{ value: "巨龙之吼", label: "巨龙之吼" },
|
||||
{ value: "索瑞森", label: "索瑞森" },
|
||||
{ value: "罗宁", label: "罗宁" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [],
|
||||
},
|
||||
}
|
||||
200
lib/data/game-data.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
export type GameConfig = {
|
||||
score: number
|
||||
versions?: Record<string, string[]> // Version -> Servers
|
||||
ranks?: Record<string, number>
|
||||
}
|
||||
|
||||
export const GAME_DATA = {
|
||||
// Game Value Configuration
|
||||
games: {
|
||||
王者荣耀: {
|
||||
score: 30,
|
||||
versions: {
|
||||
全服通用: ["微信区", "QQ区"],
|
||||
},
|
||||
ranks: {
|
||||
荣耀王者: 10,
|
||||
最强王者: 8,
|
||||
至尊星耀: 5,
|
||||
永恒钻石: 3,
|
||||
尊贵铂金: 1,
|
||||
黄金及以下: 0,
|
||||
},
|
||||
},
|
||||
原神: {
|
||||
score: 30,
|
||||
versions: {
|
||||
全服通用: ["天空岛", "世界树"],
|
||||
},
|
||||
ranks: {
|
||||
"60级": 10,
|
||||
"55-59级": 8,
|
||||
"50-54级": 5,
|
||||
"45-49级": 3,
|
||||
"45级以下": 0,
|
||||
},
|
||||
},
|
||||
和平精英: {
|
||||
score: 25,
|
||||
versions: {
|
||||
全服通用: ["微信区", "QQ区"],
|
||||
},
|
||||
ranks: {
|
||||
无敌战神: 10,
|
||||
超级王牌: 8,
|
||||
荣耀皇冠: 5,
|
||||
不朽星钻: 3,
|
||||
星钻以下: 0,
|
||||
},
|
||||
},
|
||||
魔兽世界: {
|
||||
score: 30,
|
||||
versions: {
|
||||
亚服大地的裂变: ["伊弗斯", "瑪拉頓", "阿拉希盆地", "鱼人", "逐风者", "古雷曼格", "乌苏雷", "札里克"],
|
||||
亚服正式服: [
|
||||
"暗影之月",
|
||||
"众星之子",
|
||||
"地狱吼",
|
||||
"狂热之刃",
|
||||
"阿萨斯",
|
||||
"冰霜之刺",
|
||||
"米奈希尔",
|
||||
"血之谷",
|
||||
"夜空之歌",
|
||||
"巨龙之喉",
|
||||
"世界之树",
|
||||
"屠魔山谷",
|
||||
"水晶之刺",
|
||||
],
|
||||
国服正式服: [
|
||||
"死亡之翼",
|
||||
"白银之手",
|
||||
"罗宁",
|
||||
"主宰之剑",
|
||||
"格瑞姆巴托",
|
||||
"安苏",
|
||||
"熊猫酒仙",
|
||||
"国王之谷",
|
||||
"燃烧之刃",
|
||||
"影之哀伤",
|
||||
],
|
||||
国服怀旧服: [
|
||||
"哈霍兰",
|
||||
"奥罗",
|
||||
"匕首岭",
|
||||
"布鲁",
|
||||
"范克瑞斯",
|
||||
"席瓦莱恩",
|
||||
"碧玉矿洞",
|
||||
"寒脊山小径",
|
||||
"祈福",
|
||||
"灰烬使者",
|
||||
],
|
||||
},
|
||||
ranks: {
|
||||
"角斗士/名人堂": 10,
|
||||
"高阶督军/大元帅": 8,
|
||||
"督军/元帅": 5,
|
||||
"统帅/将军": 3,
|
||||
普通: 0,
|
||||
},
|
||||
},
|
||||
英雄联盟: {
|
||||
score: 30,
|
||||
versions: {
|
||||
电信区: [
|
||||
"艾欧尼亚",
|
||||
"祖安",
|
||||
"诺克萨斯",
|
||||
"班德尔城",
|
||||
"皮城警备",
|
||||
"战争学院",
|
||||
"巨神峰",
|
||||
"雷瑟守备",
|
||||
"裁决之地",
|
||||
"黑色玫瑰",
|
||||
"暗影岛",
|
||||
"钢铁烈阳",
|
||||
"水晶之痕",
|
||||
"均衡教派",
|
||||
"影流",
|
||||
"守望之海",
|
||||
"征服之海",
|
||||
"卡拉曼达",
|
||||
"皮城警备",
|
||||
],
|
||||
网通区: ["比尔吉沃特", "德玛西亚", "弗雷尔卓德", "无畏先锋", "恕瑞玛", "扭曲丛林", "巨龙之巢"],
|
||||
},
|
||||
ranks: {
|
||||
最强王者: 10,
|
||||
傲世宗师: 8,
|
||||
超凡大师: 6,
|
||||
璀璨钻石: 4,
|
||||
华贵铂金: 2,
|
||||
铂金以下: 0,
|
||||
},
|
||||
},
|
||||
无畏契约: {
|
||||
score: 25,
|
||||
versions: {
|
||||
国服: ["重庆", "南京", "天津", "广州"],
|
||||
},
|
||||
ranks: {
|
||||
赋能战魂: 10,
|
||||
神话: 8,
|
||||
超凡入圣: 6,
|
||||
钻石: 4,
|
||||
铂金: 2,
|
||||
铂金以下: 0,
|
||||
},
|
||||
},
|
||||
"CS:GO": {
|
||||
score: 25,
|
||||
versions: {
|
||||
国服: ["完美世界"],
|
||||
},
|
||||
ranks: {
|
||||
大地球: 10,
|
||||
小地球: 8,
|
||||
老鹰: 6,
|
||||
菊花: 4,
|
||||
AK: 2,
|
||||
AK以下: 0,
|
||||
},
|
||||
},
|
||||
} as Record<string, GameConfig>,
|
||||
|
||||
// Server Value Configuration
|
||||
server_scores: {
|
||||
热门服: 10, // Hot/New servers
|
||||
普通服: 8, // Normal servers
|
||||
鬼服: 5, // Low population
|
||||
} as Record<string, number>,
|
||||
|
||||
// Asset Weight Configuration
|
||||
asset_weights: {
|
||||
传说级: 1.5, // Legendary
|
||||
史诗级: 1.0, // Epic
|
||||
优质级: 0.5, // Rare
|
||||
普通级: 0.2, // Common
|
||||
} as Record<string, number>,
|
||||
|
||||
// Risk Control Coefficient
|
||||
correction_coeffs: {
|
||||
本人_无封号: 1.2,
|
||||
本人_有封号: 1.0,
|
||||
非本人_无封号: 0.8,
|
||||
非本人_有封号: 0.6,
|
||||
未实名: 0.7,
|
||||
} as Record<string, number>,
|
||||
|
||||
consumption_weights: {
|
||||
level1: 0, // 0-100
|
||||
level2: 1.05, // 100-500
|
||||
level3: 1.1, // 500-2000
|
||||
level4: 1.2, // 2000+
|
||||
} as Record<string, number>,
|
||||
|
||||
// Base multiplier for final valuation (converts score to currency)
|
||||
base_multiple: 100,
|
||||
}
|
||||
133
lib/utils/account-evaluation.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { GAME_DATA } from "@/lib/data/game-data"
|
||||
|
||||
export type EvaluationInput = {
|
||||
gameName: string
|
||||
serverType: string // "热门服" | "普通服" | "鬼服"
|
||||
currentLevel: number
|
||||
maxLevel: number
|
||||
rankName: string
|
||||
|
||||
// Assets
|
||||
assetCounts: {
|
||||
legendary: number // 传说级
|
||||
epic: number // 史诗级
|
||||
rare: number // 优质级
|
||||
common: number // 普通级
|
||||
}
|
||||
|
||||
// Bonus
|
||||
registrationMonths: number
|
||||
rechargeAmount6Months: number
|
||||
mallConsumption?: number // Added mall consumption parameter
|
||||
|
||||
// Risk
|
||||
isRealNameVerified: boolean
|
||||
isRealNameOwner: boolean
|
||||
hasBanRecord: boolean
|
||||
}
|
||||
|
||||
export type EvaluationResult = {
|
||||
totalScore: number
|
||||
valuation: number // Preliminary valuation
|
||||
loanAmount: number // Loan amount
|
||||
details: {
|
||||
baseScore: number
|
||||
assetScore: number
|
||||
bonusScore: number
|
||||
riskCoefficient: number
|
||||
consumptionMultiplier: number // Added consumption multiplier to result
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateAccount(input: EvaluationInput): EvaluationResult {
|
||||
const gameConfig = GAME_DATA.games[input.gameName] || { score: 20, ranks: {} }
|
||||
|
||||
// 1. Base Value Score (Max 50)
|
||||
// Game Score (Max 30)
|
||||
const gameScore = gameConfig.score
|
||||
|
||||
// Server Score (Max 10)
|
||||
const serverScore = GAME_DATA.server_scores[input.serverType] || 8
|
||||
|
||||
// Level Score (Max 10)
|
||||
// Score = (Current / Max) * 10
|
||||
const levelRatio = Math.min(input.currentLevel / (input.maxLevel || 100), 1)
|
||||
const levelScore = Number.parseFloat((levelRatio * 10).toFixed(1))
|
||||
|
||||
const baseScore = gameScore + serverScore + levelScore
|
||||
|
||||
// 2. Core Asset Score (Max 30)
|
||||
// Asset Score = Sum(Count * Weight)
|
||||
// Cap count at 20 to avoid outliers as per requirements
|
||||
const getAssetScore = (count: number, weight: number) => Math.min(count, 20) * weight
|
||||
|
||||
const assetBaseScore =
|
||||
getAssetScore(input.assetCounts.legendary, GAME_DATA.asset_weights["传说级"]) +
|
||||
getAssetScore(input.assetCounts.epic, GAME_DATA.asset_weights["史诗级"]) +
|
||||
getAssetScore(input.assetCounts.rare, GAME_DATA.asset_weights["优质级"]) +
|
||||
getAssetScore(input.assetCounts.common, GAME_DATA.asset_weights["普通级"])
|
||||
|
||||
// Rank Bonus
|
||||
const rankBonus = gameConfig.ranks ? gameConfig.ranks[input.rankName] || 0 : 0
|
||||
|
||||
const coreAssetScore = Math.min(assetBaseScore + rankBonus, 30)
|
||||
|
||||
// 3. Bonus Score (Max 20)
|
||||
let timeScore = 0
|
||||
if (input.registrationMonths >= 25) timeScore = 10
|
||||
else if (input.registrationMonths >= 13) timeScore = 8
|
||||
else if (input.registrationMonths >= 7) timeScore = 5
|
||||
else timeScore = 3
|
||||
|
||||
// Recharge Amount (Max 10)
|
||||
// Score = min(Amount * 0.001, 10)
|
||||
const rechargeScore = Math.min(input.rechargeAmount6Months * 0.001, 10)
|
||||
|
||||
const bonusScore = timeScore + rechargeScore
|
||||
|
||||
// 4. Risk Correction Coefficient
|
||||
let riskKey = "未实名"
|
||||
if (input.isRealNameVerified) {
|
||||
if (input.isRealNameOwner) {
|
||||
riskKey = input.hasBanRecord ? "本人_有封号" : "本人_无封号"
|
||||
} else {
|
||||
riskKey = input.hasBanRecord ? "非本人_有封号" : "非本人_无封号"
|
||||
}
|
||||
}
|
||||
|
||||
const riskCoefficient = GAME_DATA.correction_coeffs[riskKey] || 0.7
|
||||
|
||||
let consumptionMultiplier = 1.0
|
||||
const consumption = input.mallConsumption || 0
|
||||
if (consumption >= 2000) {
|
||||
consumptionMultiplier = GAME_DATA.consumption_weights.level4
|
||||
} else if (consumption >= 500) {
|
||||
consumptionMultiplier = GAME_DATA.consumption_weights.level3
|
||||
} else if (consumption >= 100) {
|
||||
consumptionMultiplier = GAME_DATA.consumption_weights.level2
|
||||
} else {
|
||||
consumptionMultiplier = 1.0
|
||||
}
|
||||
|
||||
// Final Calculation
|
||||
const totalScore = Number.parseFloat(((baseScore + coreAssetScore + bonusScore) * riskCoefficient).toFixed(2))
|
||||
const valuation = Math.floor(totalScore * GAME_DATA.base_multiple)
|
||||
|
||||
let loanAmount = Math.floor(valuation * 0.6 * consumptionMultiplier)
|
||||
if (loanAmount < 500) {
|
||||
loanAmount = 0
|
||||
}
|
||||
|
||||
return {
|
||||
totalScore,
|
||||
valuation,
|
||||
loanAmount,
|
||||
details: {
|
||||
baseScore: Number.parseFloat(baseScore.toFixed(1)),
|
||||
assetScore: Number.parseFloat(coreAssetScore.toFixed(1)),
|
||||
bonusScore: Number.parseFloat(bonusScore.toFixed(1)),
|
||||
riskCoefficient,
|
||||
consumptionMultiplier, // Return multiplier for UI display
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 196 KiB |
BIN
public/account-genshin.jpg
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
public/account-hok.jpg
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
public/account-icon-gen.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/account-icon-hok.jpg
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
public/account-icon-lol.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/account-icon-val.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
public/account-lol.jpg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
public/account-val.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 79 KiB |
BIN
public/avatar-3.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
public/avatar-4.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
public/avatar-5.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/avatar-6.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
public/avatar-assassin.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/avatar-cute.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
public/avatar-cyberpunk.jpg
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
public/avatar-mecha.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
public/badge-gold.jpg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
public/club-logo-0.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/club-logo-1.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/club-logo-2.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/companion-cover-1.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/companion-cover-2.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/course-1.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/course-2.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/course-3.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/course-4.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/effect-neon.jpg
Normal file
|
After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 232 KiB |
BIN
public/feed-live-1101.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/feed-live-1102.jpg
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
public/feed-live-1103.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
public/feed-live-1104.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
public/frame-starry.jpg
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
public/gamer-profile-avatar.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
public/gear-headset.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/gear-keyboard.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/gear-monitor.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/gear-mouse.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/guild-logo-0.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/guild-logo-1.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/guild-logo-2.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/guild-logo-3.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
public/recharge-banner.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/tournament-event-today.jpg
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
public/user-avatar.jpg
Normal file
|
After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 134 KiB |