refactor: complete system overhaul with new design
Rebuilt user and match pages, simplified login, and updated bottom navigation. #VERCEL_SKIP Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useState } from "react"
|
||||
import { motion, AnimatePresence } from "framer-motion"
|
||||
import { Mic } from "lucide-react"
|
||||
|
||||
interface MatchUser {
|
||||
id: string
|
||||
@@ -14,56 +15,62 @@ interface MatchUser {
|
||||
commonInterests: Array<{ icon: string; text: string }>
|
||||
}
|
||||
|
||||
const matchTypes = [
|
||||
{ id: "partner", label: "创业合伙", icon: "⭐", color: "#00E5FF" },
|
||||
{ id: "investor", label: "资源对接", icon: "👥", color: "#7B61FF" },
|
||||
{ id: "mentor", label: "导师顾问", icon: "❤️", color: "#E91E63" },
|
||||
{ id: "team", label: "团队招募", icon: "🎮", color: "#4CAF50" },
|
||||
]
|
||||
|
||||
export default function MatchPage() {
|
||||
const [isMatching, setIsMatching] = useState(false)
|
||||
const [currentMatch, setCurrentMatch] = useState<MatchUser | null>(null)
|
||||
const [matchAttempts, setMatchAttempts] = useState(0)
|
||||
const [selectedType, setSelectedType] = useState("partner")
|
||||
const [activeTab, setActiveTab] = useState<"voice" | "party" | "team">("voice")
|
||||
|
||||
const startMatch = () => {
|
||||
setIsMatching(true)
|
||||
setMatchAttempts(0)
|
||||
setCurrentMatch(null)
|
||||
|
||||
// 模拟匹配过程
|
||||
const interval = setInterval(() => {
|
||||
setMatchAttempts((prev) => prev + 1)
|
||||
}, 1000)
|
||||
|
||||
// 3-6秒后匹配成功
|
||||
setTimeout(() => {
|
||||
setTimeout(
|
||||
() => {
|
||||
clearInterval(interval)
|
||||
setIsMatching(false)
|
||||
setCurrentMatch(getMockMatch())
|
||||
}, Math.random() * 3000 + 3000)
|
||||
},
|
||||
Math.random() * 3000 + 3000,
|
||||
)
|
||||
}
|
||||
|
||||
const getMockMatch = (): MatchUser => {
|
||||
const nicknames = ['阅读爱好者', '创业小白', '私域达人', '书虫一枚', '灵魂摆渡人']
|
||||
const nicknames = ["创业先锋", "资源整合者", "私域专家", "商业导师", "连续创业者"]
|
||||
const randomIndex = Math.floor(Math.random() * nicknames.length)
|
||||
const concepts = [
|
||||
'一个坚持长期主义的私域玩家,擅长内容结构化。',
|
||||
'相信阅读可以改变人生,每天坚持读书1小时。',
|
||||
'在Soul上分享创业经验,希望帮助更多人少走弯路。'
|
||||
]
|
||||
const wechats = [
|
||||
'soul_book_friend_1',
|
||||
'soul_reader_2024',
|
||||
'soul_party_fan'
|
||||
"专注私域流量运营5年,帮助100+品牌实现从0到1的增长。",
|
||||
"连续创业者,擅长商业模式设计和资源整合。",
|
||||
"在Soul分享真实创业故事,希望找到志同道合的合作伙伴。",
|
||||
]
|
||||
const wechats = ["soul_partner_1", "soul_business_2024", "soul_startup_fan"]
|
||||
|
||||
return {
|
||||
id: `user_${Date.now()}`,
|
||||
nickname: nicknames[randomIndex],
|
||||
avatar: `https://picsum.photos/200/200?random=${randomIndex}`,
|
||||
tags: ['创业者', '私域运营', 'MBTI-INTP'],
|
||||
tags: ["创业者", "私域运营", matchTypes.find((t) => t.id === selectedType)?.label || ""],
|
||||
matchScore: Math.floor(Math.random() * 20) + 80,
|
||||
concept: concepts[randomIndex % concepts.length],
|
||||
wechat: wechats[randomIndex % wechats.length],
|
||||
commonInterests: [
|
||||
{ icon: '📚', text: '都在读《创业实验》' },
|
||||
{ icon: '💼', text: '对私域运营感兴趣' },
|
||||
{ icon: '🎯', text: '相似的职业背景' }
|
||||
]
|
||||
{ icon: "📚", text: "都在读《创业实验》" },
|
||||
{ icon: "💼", text: "对私域运营感兴趣" },
|
||||
{ icon: "🎯", text: "相似的创业方向" },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,56 +81,63 @@ export default function MatchPage() {
|
||||
|
||||
const handleAddWechat = () => {
|
||||
if (!currentMatch) return
|
||||
|
||||
// 复制微信号
|
||||
navigator.clipboard.writeText(currentMatch.wechat).then(() => {
|
||||
alert(`微信号已复制:${currentMatch.wechat}\n\n请打开微信添加好友,备注"书友"即可。`)
|
||||
}).catch(() => {
|
||||
navigator.clipboard
|
||||
.writeText(currentMatch.wechat)
|
||||
.then(() => {
|
||||
alert(`微信号已复制:${currentMatch.wechat}\n\n请打开微信添加好友,备注"创业合作"即可。`)
|
||||
})
|
||||
.catch(() => {
|
||||
alert(`微信号:${currentMatch.wechat}\n\n请手动复制并添加好友。`)
|
||||
})
|
||||
}
|
||||
|
||||
const handleJoinGroup = () => {
|
||||
alert('请先添加书友微信,备注"书友群",对方会拉你入群。\n\n群内可以:\n· 深度交流读书心得\n· 参加线下读书会\n· 获取独家资源')
|
||||
}
|
||||
const currentTypeLabel = matchTypes.find((t) => t.id === selectedType)?.label || "创业合伙"
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-black pb-20 page-transition">
|
||||
{/* 星空背景 */}
|
||||
<div className="fixed inset-0 overflow-hidden pointer-events-none">
|
||||
{Array.from({ length: 100 }).map((_, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="absolute w-1 h-1 bg-white rounded-full"
|
||||
style={{
|
||||
left: `${Math.random() * 100}%`,
|
||||
top: `${Math.random() * 100}%`,
|
||||
opacity: Math.random() * 0.7 + 0.3,
|
||||
}}
|
||||
animate={{
|
||||
opacity: [0.3, 1, 0.3],
|
||||
scale: [1, 1.5, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: Math.random() * 3 + 2,
|
||||
repeat: Infinity,
|
||||
delay: Math.random() * 2,
|
||||
}}
|
||||
<div className="min-h-screen bg-black pb-24">
|
||||
{/* 顶部标题 */}
|
||||
<div className="flex items-center justify-between px-6 pt-6 pb-4">
|
||||
<h1 className="text-2xl font-bold text-white">星球</h1>
|
||||
<button className="w-10 h-10 rounded-full bg-[#1c1c1e] flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-white/60" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10">
|
||||
{/* 头部 */}
|
||||
<div className="px-6 pt-20 pb-8 text-center">
|
||||
<h1 className="text-5xl font-bold text-white mb-4">寻找合作伙伴</h1>
|
||||
<p className="text-white/60 text-lg">
|
||||
找到和你一起创业的灵魂
|
||||
</p>
|
||||
{/* Tab切换 */}
|
||||
<div className="mx-6 mb-8 p-1 bg-[#1c1c1e] rounded-full flex">
|
||||
<button
|
||||
onClick={() => setActiveTab("voice")}
|
||||
className={`flex-1 py-3 rounded-full text-sm font-medium transition-all ${
|
||||
activeTab === "voice" ? "bg-[#00E5FF] text-black" : "text-white/60"
|
||||
}`}
|
||||
>
|
||||
语音匹配
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("party")}
|
||||
className={`flex-1 py-3 rounded-full text-sm font-medium transition-all ${
|
||||
activeTab === "party" ? "bg-[#00E5FF] text-black" : "text-white/60"
|
||||
}`}
|
||||
>
|
||||
派对
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("team")}
|
||||
className={`flex-1 py-3 rounded-full text-sm font-medium transition-all ${
|
||||
activeTab === "team" ? "bg-[#00E5FF] text-black" : "text-white/60"
|
||||
}`}
|
||||
>
|
||||
开黑
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 匹配状态区 */}
|
||||
<div className="px-6 pt-10">
|
||||
<AnimatePresence mode="wait">
|
||||
{!isMatching && !currentMatch && (
|
||||
<motion.div
|
||||
@@ -131,66 +145,104 @@ export default function MatchPage() {
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className="flex flex-col items-center"
|
||||
className="flex flex-col items-center px-6"
|
||||
>
|
||||
{/* 中央大星球 */}
|
||||
{/* 中央匹配圆环 */}
|
||||
<motion.div
|
||||
onClick={startMatch}
|
||||
className="relative w-[280px] h-[280px] mb-12 cursor-pointer"
|
||||
className="relative w-[280px] h-[280px] mb-8 cursor-pointer"
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
{/* 外层光环 */}
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-full flex flex-col items-center justify-center"
|
||||
className="absolute inset-[-30px] rounded-full"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #00E5FF 0%, #7B61FF 50%, #E91E63 100%)',
|
||||
boxShadow: '0 0 60px rgba(0, 229, 255, 0.4), 0 0 120px rgba(123, 97, 255, 0.3), inset 0 0 80px rgba(255, 255, 255, 0.1)'
|
||||
background: "radial-gradient(circle, transparent 50%, rgba(0, 229, 255, 0.1) 70%, transparent 100%)",
|
||||
}}
|
||||
animate={{
|
||||
y: [0, -10, 0],
|
||||
scale: [1, 1.02, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<div className="text-6xl mb-3 filter brightness-0 invert">🤝</div>
|
||||
<div className="text-2xl font-bold text-white mb-2 drop-shadow-lg">开始匹配</div>
|
||||
<div className="text-sm text-white/90 drop-shadow">寻找合作伙伴</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="absolute inset-0 border-2 border-[#00E5FF]/30 rounded-full"
|
||||
style={{ width: '330px', height: '330px', left: '-25px', top: '-25px' }}
|
||||
animate={{
|
||||
opacity: [0.3, 0.6, 0.3],
|
||||
scale: [1, 1.05, 1],
|
||||
scale: [1, 1.1, 1],
|
||||
opacity: [0.5, 0.8, 0.5],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 中间光环 */}
|
||||
<motion.div
|
||||
className="absolute inset-[-15px] rounded-full border-2 border-[#00E5FF]/30"
|
||||
animate={{
|
||||
scale: [1, 1.05, 1],
|
||||
opacity: [0.3, 0.6, 0.3],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.5,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 内层渐变球 */}
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-full flex flex-col items-center justify-center overflow-hidden"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)",
|
||||
boxShadow: "0 0 60px rgba(0, 229, 255, 0.3), inset 0 0 60px rgba(123, 97, 255, 0.2)",
|
||||
}}
|
||||
animate={{
|
||||
y: [0, -5, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
{/* 内部渐变光效 */}
|
||||
<div
|
||||
className="absolute inset-0 rounded-full"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(circle at 30% 30%, rgba(123, 97, 255, 0.4) 0%, transparent 50%), radial-gradient(circle at 70% 70%, rgba(233, 30, 99, 0.3) 0%, transparent 50%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 中心图标 */}
|
||||
<Mic className="w-12 h-12 text-white/90 mb-3 relative z-10" />
|
||||
<div className="text-xl font-bold text-white mb-1 relative z-10">开始匹配</div>
|
||||
<div className="text-sm text-white/60 relative z-10">寻找{currentTypeLabel}</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{/* 匹配提示 */}
|
||||
<div className="glass-card p-6 mb-8 w-full max-w-md">
|
||||
<div className="space-y-3 text-white/70">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-2xl">💼</span>
|
||||
<span>共同的创业方向</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-2xl">💬</span>
|
||||
<span>实时在线交流</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-2xl">🎯</span>
|
||||
<span>相似的商业洞察</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* 当前模式显示 */}
|
||||
<p className="text-white/50 text-sm mb-8">
|
||||
当前模式: <span className="text-[#00E5FF]">{currentTypeLabel}</span>
|
||||
</p>
|
||||
|
||||
{/* 分隔线 */}
|
||||
<div className="w-full h-px bg-white/10 mb-6" />
|
||||
|
||||
{/* 选择匹配类型 */}
|
||||
<p className="text-white/40 text-sm mb-4">选择匹配类型</p>
|
||||
<div className="grid grid-cols-4 gap-3 w-full">
|
||||
{matchTypes.map((type) => (
|
||||
<button
|
||||
key={type.id}
|
||||
onClick={() => setSelectedType(type.id)}
|
||||
className={`p-4 rounded-xl flex flex-col items-center gap-2 transition-all ${
|
||||
selectedType === type.id
|
||||
? "bg-[#00E5FF]/10 border border-[#00E5FF]/50"
|
||||
: "bg-[#1c1c1e] border border-transparent"
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl">{type.icon}</span>
|
||||
<span className={`text-xs ${selectedType === type.id ? "text-[#00E5FF]" : "text-white/60"}`}>
|
||||
{type.label}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -201,43 +253,48 @@ export default function MatchPage() {
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.9 }}
|
||||
className="text-center"
|
||||
className="text-center px-6"
|
||||
>
|
||||
{/* 匹配动画 */}
|
||||
<div className="relative w-[200px] h-[200px] mx-auto mb-8">
|
||||
<motion.div
|
||||
className="text-9xl mb-8 relative mx-auto w-fit"
|
||||
className="absolute inset-0 rounded-full bg-gradient-to-br from-[#00E5FF] via-[#7B61FF] to-[#E91E63]"
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
|
||||
transition={{ duration: 3, repeat: Number.POSITIVE_INFINITY, ease: "linear" }}
|
||||
/>
|
||||
<div className="absolute inset-2 rounded-full bg-black flex items-center justify-center">
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.2, 1] }}
|
||||
transition={{ duration: 1, repeat: Number.POSITIVE_INFINITY }}
|
||||
>
|
||||
🌍
|
||||
<Mic className="w-12 h-12 text-[#00E5FF]" />
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* 扩散波纹 */}
|
||||
{[1, 2, 3].map((ring) => (
|
||||
<motion.div
|
||||
key={ring}
|
||||
className="absolute inset-0 border-4 border-[#00E5FF]/30 rounded-full"
|
||||
style={{ width: '300px', height: '300px' }}
|
||||
className="absolute inset-0 rounded-full border-2 border-[#00E5FF]/30"
|
||||
animate={{
|
||||
scale: [1, 2, 1],
|
||||
opacity: [1, 0, 1],
|
||||
scale: [1, 2],
|
||||
opacity: [0.6, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
delay: ring * 0.6,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
delay: ring * 0.5,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-4 text-white">
|
||||
正在寻找志同道合的书友...
|
||||
</h2>
|
||||
<p className="text-white/50 mb-8">
|
||||
已匹配 {matchAttempts} 次
|
||||
</p>
|
||||
<h2 className="text-xl font-semibold mb-2 text-white">正在寻找合作伙伴...</h2>
|
||||
<p className="text-white/50 mb-8">已匹配 {matchAttempts} 次</p>
|
||||
|
||||
<button
|
||||
onClick={() => setIsMatching(false)}
|
||||
className="btn-ios-secondary"
|
||||
className="px-8 py-3 rounded-full bg-[#1c1c1e] text-white border border-white/10"
|
||||
>
|
||||
取消匹配
|
||||
</button>
|
||||
@@ -250,63 +307,49 @@ export default function MatchPage() {
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className="max-w-md mx-auto"
|
||||
className="px-6"
|
||||
>
|
||||
{/* 成功动画 */}
|
||||
<motion.div
|
||||
className="text-center mb-8"
|
||||
className="text-center mb-6"
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: "spring", bounce: 0.5 }}
|
||||
>
|
||||
<span className="text-9xl">✨</span>
|
||||
<span className="text-6xl">✨</span>
|
||||
</motion.div>
|
||||
|
||||
{/* 用户卡片 */}
|
||||
<div className="glass-card p-6 mb-6">
|
||||
<div className="bg-[#1c1c1e] rounded-2xl p-5 mb-4 border border-white/5">
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<img
|
||||
src={currentMatch.avatar}
|
||||
src={currentMatch.avatar || "/placeholder.svg"}
|
||||
alt={currentMatch.nickname}
|
||||
className="w-20 h-20 rounded-full border-4 border-[#00E5FF]"
|
||||
className="w-16 h-16 rounded-full border-2 border-[#00E5FF]"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-2xl font-semibold mb-2 text-white">
|
||||
{currentMatch.nickname}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<h3 className="text-lg font-semibold text-white mb-2">{currentMatch.nickname}</h3>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{currentMatch.tags.map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="px-3 py-1 rounded-full text-sm bg-[#00E5FF]/20 text-[#00E5FF]"
|
||||
>
|
||||
<span key={tag} className="px-2 py-0.5 rounded text-xs bg-[#00E5FF]/20 text-[#00E5FF]">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold text-[#00E5FF]">
|
||||
{currentMatch.matchScore}%
|
||||
</div>
|
||||
<div className="text-xs text-white/50">
|
||||
匹配度
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-[#00E5FF]">{currentMatch.matchScore}%</div>
|
||||
<div className="text-xs text-white/50">匹配度</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 共同兴趣 */}
|
||||
<div className="pt-4 border-t border-white/10 mb-4">
|
||||
<h4 className="text-sm text-white/60 mb-3">
|
||||
共同兴趣
|
||||
</h4>
|
||||
<h4 className="text-sm text-white/60 mb-2">共同兴趣</h4>
|
||||
<div className="space-y-2">
|
||||
{currentMatch.commonInterests.map((interest, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 text-sm text-white/80"
|
||||
>
|
||||
<span className="text-xl">{interest.icon}</span>
|
||||
<div key={i} className="flex items-center gap-2 text-sm text-white/80">
|
||||
<span>{interest.icon}</span>
|
||||
<span>{interest.text}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -315,45 +358,26 @@ export default function MatchPage() {
|
||||
|
||||
{/* 核心理念 */}
|
||||
<div className="pt-4 border-t border-white/10">
|
||||
<h4 className="text-sm text-white/60 mb-3">
|
||||
核心理念
|
||||
</h4>
|
||||
<p className="text-sm text-white/70 leading-relaxed">
|
||||
{currentMatch.concept}
|
||||
</p>
|
||||
<h4 className="text-sm text-white/60 mb-2">核心理念</h4>
|
||||
<p className="text-sm text-white/70">{currentMatch.concept}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={handleAddWechat}
|
||||
className="btn-ios flex-1 flex items-center justify-center gap-2"
|
||||
>
|
||||
<span className="text-xl">➕</span>
|
||||
<span>一键加好友</span>
|
||||
<div className="space-y-3">
|
||||
<button onClick={handleAddWechat} className="w-full py-4 rounded-xl bg-[#00E5FF] text-black font-medium">
|
||||
➕ 一键加好友
|
||||
</button>
|
||||
<button
|
||||
onClick={handleJoinGroup}
|
||||
className="btn-ios flex-1 flex items-center justify-center gap-2"
|
||||
>
|
||||
<span className="text-xl">👥</span>
|
||||
<span>加入书友群</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={nextMatch}
|
||||
className="btn-ios-secondary w-full"
|
||||
className="w-full py-4 rounded-xl bg-[#1c1c1e] text-white border border-white/10"
|
||||
>
|
||||
🔄 不喜欢?重新匹配
|
||||
🔄 重新匹配
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
460
app/my/page.tsx
460
app/my/page.tsx
@@ -2,15 +2,16 @@
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import Link from "next/link"
|
||||
import { User, ShoppingBag, Share2, LogOut, ChevronRight, BookOpen } from "lucide-react"
|
||||
import { User, ChevronRight, Copy, Check } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
import { AuthModal } from "@/components/modules/auth/auth-modal"
|
||||
import { getFullBookPrice } from "@/lib/book-data"
|
||||
import { getFullBookPrice, getTotalSectionCount } from "@/lib/book-data"
|
||||
|
||||
export default function MyPage() {
|
||||
const { user, isLoggedIn, logout } = useStore()
|
||||
const { user, isLoggedIn, logout, getAllPurchases } = useStore()
|
||||
const [showAuthModal, setShowAuthModal] = useState(false)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
@@ -18,134 +19,403 @@ export default function MyPage() {
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div className="min-h-screen bg-app-bg flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-app-brand" />
|
||||
<div className="min-h-screen bg-black flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-[#ff3b5c]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<main className="min-h-screen bg-app-bg text-app-text pb-20 flex flex-col items-center justify-center">
|
||||
<div className="p-4 w-full">
|
||||
<div className="max-w-xs mx-auto text-center">
|
||||
<div className="w-14 h-14 mx-auto mb-3 rounded-full bg-app-card flex items-center justify-center">
|
||||
<User className="w-7 h-7 text-app-text-muted" />
|
||||
</div>
|
||||
<h2 className="text-base font-semibold mb-1">登录后查看更多</h2>
|
||||
<p className="text-app-text-muted text-xs mb-4">查看购买记录、分销收益</p>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="bg-app-brand hover:bg-app-brand-hover text-white px-6 py-2.5 rounded-full font-medium text-sm"
|
||||
>
|
||||
立即登录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<AuthModal isOpen={showAuthModal} onClose={() => setShowAuthModal(false)} />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
const fullBookPrice = getFullBookPrice()
|
||||
const totalSections = getTotalSectionCount()
|
||||
const purchasedCount = user?.hasFullBook ? totalSections : user?.purchasedSections?.length || 0
|
||||
|
||||
// 计算阅读时长(模拟数据,实际应该从localStorage读取)
|
||||
const readingMinutes = Math.floor(Math.random() * 100)
|
||||
const bookmarks = user?.purchasedSections?.length || 0
|
||||
|
||||
const handleCopyCode = () => {
|
||||
if (user?.referralCode) {
|
||||
navigator.clipboard.writeText(user.referralCode)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
// 未登录状态
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<main className="min-h-screen bg-app-bg text-app-text pb-20">
|
||||
{/* User Profile Header */}
|
||||
<div className="bg-gradient-to-b from-app-card to-app-bg p-4 pt-8">
|
||||
<div className="max-w-xs mx-auto">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-11 h-11 rounded-full bg-app-brand/20 flex items-center justify-center">
|
||||
<User className="w-5 h-5 text-app-brand" />
|
||||
<main className="min-h-screen bg-black text-white pb-24">
|
||||
{/* 顶部标题 */}
|
||||
<div className="text-center py-4 border-b border-white/10">
|
||||
<h1 className="text-lg font-medium text-[#ffd700]">Soul派对·创业实验</h1>
|
||||
</div>
|
||||
|
||||
{/* 用户卡片 - 未登录 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-14 h-14 rounded-full border-2 border-[#ff3b5c]/50 flex items-center justify-center">
|
||||
<User className="w-7 h-7 text-white/30" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold">{user?.nickname || "用户"}</h2>
|
||||
<p className="text-app-text-muted text-xs">{user?.phone}</p>
|
||||
<button onClick={() => setShowAuthModal(true)} className="text-[#ff3b5c] font-medium text-lg">
|
||||
点击登录
|
||||
</button>
|
||||
<p className="text-white/30 text-sm">ID: ---</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="bg-app-card/60 rounded-lg p-2.5 text-center">
|
||||
<p className="text-base font-bold text-app-brand">
|
||||
{user?.hasFullBook ? "全部" : user?.purchasedSections.length || 0}
|
||||
</p>
|
||||
<p className="text-app-text-muted text-xs">已购章节</p>
|
||||
{/* 统计数据 */}
|
||||
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">已读章节</p>
|
||||
</div>
|
||||
<div className="bg-app-card/60 rounded-lg p-2.5 text-center">
|
||||
<p className="text-base font-bold text-app-brand">¥{(user?.earnings || 0).toFixed(1)}</p>
|
||||
<p className="text-app-text-muted text-xs">累计收益</p>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">阅读时长(分)</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">书签</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Menu Items */}
|
||||
<div className="p-4">
|
||||
<div className="max-w-xs mx-auto space-y-2">
|
||||
{/* Purchase prompt */}
|
||||
{!user?.hasFullBook && (
|
||||
<Link href="/chapters" className="block">
|
||||
<div className="bg-gradient-to-r from-app-brand/20 to-app-card rounded-lg p-3 border border-app-brand/30">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* 分销中心 - 未登录 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<BookOpen className="w-4 h-4 text-app-brand" />
|
||||
<span className="text-app-text text-sm">购买整本书</span>
|
||||
<span className="text-xl">💰</span>
|
||||
<span className="text-white font-medium">分销中心</span>
|
||||
</div>
|
||||
<span className="text-app-brand font-bold text-sm">¥{fullBookPrice}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Menu List - simplified, removed settings and docs */}
|
||||
<div className="bg-app-card/60 rounded-lg overflow-hidden">
|
||||
<Link href="/my/purchases" className="flex items-center justify-between p-3 border-b border-app-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<ShoppingBag className="w-4 h-4 text-app-text-muted" />
|
||||
<span className="text-sm">我的购买</span>
|
||||
</div>
|
||||
<ChevronRight className="w-4 h-4 text-app-text-muted" />
|
||||
</Link>
|
||||
|
||||
<Link href="/my/referral" className="flex items-center justify-between p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Share2 className="w-4 h-4 text-app-text-muted" />
|
||||
<span className="text-sm">分销收益</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-app-brand text-xs">¥{(user?.earnings || 0).toFixed(1)}</span>
|
||||
<ChevronRight className="w-4 h-4 text-app-text-muted" />
|
||||
</div>
|
||||
</Link>
|
||||
<span className="text-[#ff3b5c] text-sm bg-[#ff3b5c]/10 px-2 py-1 rounded">佣金比例: 90%</span>
|
||||
</div>
|
||||
|
||||
{/* Referral Code */}
|
||||
<div className="bg-app-card/60 rounded-lg p-3">
|
||||
{/* 收益卡片 */}
|
||||
<div className="bg-[#2c2c2e] rounded-xl p-4 mb-4">
|
||||
<p className="text-white/50 text-sm text-center mb-2">累计收益</p>
|
||||
<p className="text-[#ffd700] text-3xl font-bold text-center">¥0.00</p>
|
||||
<div className="grid grid-cols-2 gap-4 mt-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-white/50 text-xs">可提现</p>
|
||||
<p className="text-white font-medium">¥0.00</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-white/50 text-xs">已提现</p>
|
||||
<p className="text-white font-medium">¥0.00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 按钮 */}
|
||||
<div className="grid grid-cols-2 gap-3 mb-4">
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="py-3 rounded-xl bg-[#ff3b5c] text-white font-medium"
|
||||
>
|
||||
生成推广海报
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="py-3 rounded-xl bg-[#2c2c2e] text-white font-medium border border-white/10"
|
||||
>
|
||||
立即提现
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 统计 */}
|
||||
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">推荐人数</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">成交订单</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">90%</p>
|
||||
<p className="text-white/40 text-xs">佣金率</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 邀请码 */}
|
||||
<div className="mt-4 pt-4 border-t border-white/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-app-text-muted text-xs">我的邀请码</p>
|
||||
<code className="text-app-brand font-mono text-sm">{user?.referralCode}</code>
|
||||
<p className="text-white/50 text-xs">我的邀请码</p>
|
||||
<p className="text-[#ff3b5c] font-mono text-lg">- - -</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => navigator.clipboard.writeText(user?.referralCode || "")}
|
||||
className="text-app-text-muted text-xs hover:text-app-text px-2 py-1 rounded bg-app-card"
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="px-4 py-2 rounded-lg bg-[#2c2c2e] text-white text-sm"
|
||||
>
|
||||
复制
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Logout */}
|
||||
<button
|
||||
onClick={logout}
|
||||
className="w-full flex items-center justify-center gap-2 p-2.5 text-app-text-muted hover:text-red-400 transition-colors text-sm"
|
||||
{/* 菜单列表 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<Link
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setShowAuthModal(true)
|
||||
}}
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
<span>退出登录</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📦</span>
|
||||
<span className="text-white">我的订单</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setShowAuthModal(true)
|
||||
}}
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">🏷️</span>
|
||||
<span className="text-white">我的书签</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setShowAuthModal(true)
|
||||
}}
|
||||
className="flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📝</span>
|
||||
<span className="text-white">阅读笔记</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 设置菜单 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<Link href="#" className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">⚙️</span>
|
||||
<span className="text-white">设置</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link href="#" className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">💬</span>
|
||||
<span className="text-white">联系客服</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link href="#" className="flex items-center justify-between p-4 active:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">ℹ️</span>
|
||||
<span className="text-white">关于我们</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<AuthModal isOpen={showAuthModal} onClose={() => setShowAuthModal(false)} />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
// 已登录状态
|
||||
const userPurchases = getAllPurchases().filter((p) => p.userId === user?.id)
|
||||
const completedOrders = userPurchases.filter((p) => p.status === "completed").length
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-black text-white pb-24">
|
||||
{/* 顶部标题 */}
|
||||
<div className="text-center py-4 border-b border-white/10">
|
||||
<h1 className="text-lg font-medium text-[#ffd700]">Soul派对·创业实验</h1>
|
||||
</div>
|
||||
|
||||
{/* 用户卡片 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-14 h-14 rounded-full border-2 border-[#ff3b5c] flex items-center justify-center bg-gradient-to-br from-[#ff3b5c]/20 to-transparent">
|
||||
<User className="w-7 h-7 text-[#ff3b5c]" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white font-medium text-lg">{user?.nickname || "用户"}</p>
|
||||
<p className="text-white/30 text-sm">ID: {user?.id?.slice(-8) || "---"}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 统计数据 */}
|
||||
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">{purchasedCount}</p>
|
||||
<p className="text-white/40 text-xs">已读章节</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">{readingMinutes}</p>
|
||||
<p className="text-white/40 text-xs">阅读时长(分)</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">{bookmarks}</p>
|
||||
<p className="text-white/40 text-xs">书签</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 分销中心 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xl">💰</span>
|
||||
<span className="text-white font-medium">分销中心</span>
|
||||
</div>
|
||||
<span className="text-[#ff3b5c] text-sm bg-[#ff3b5c]/10 px-2 py-1 rounded">佣金比例: 90%</span>
|
||||
</div>
|
||||
|
||||
{/* 收益卡片 */}
|
||||
<div className="bg-[#2c2c2e] rounded-xl p-4 mb-4">
|
||||
<p className="text-white/50 text-sm text-center mb-2">累计收益</p>
|
||||
<p className="text-[#ffd700] text-3xl font-bold text-center">¥{(user?.earnings || 0).toFixed(2)}</p>
|
||||
<div className="grid grid-cols-2 gap-4 mt-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-white/50 text-xs">可提现</p>
|
||||
<p className="text-white font-medium">¥{(user?.pendingEarnings || 0).toFixed(2)}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-white/50 text-xs">已提现</p>
|
||||
<p className="text-white font-medium">¥{(user?.withdrawnEarnings || 0).toFixed(2)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 按钮 */}
|
||||
<div className="grid grid-cols-2 gap-3 mb-4">
|
||||
<Link href="/my/referral" className="py-3 rounded-xl bg-[#ff3b5c] text-white font-medium text-center">
|
||||
生成推广海报
|
||||
</Link>
|
||||
<Link
|
||||
href="/my/referral"
|
||||
className="py-3 rounded-xl bg-[#2c2c2e] text-white font-medium text-center border border-white/10"
|
||||
>
|
||||
立即提现
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 统计 */}
|
||||
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">{user?.referralCount || 0}</p>
|
||||
<p className="text-white/40 text-xs">推荐人数</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">{completedOrders}</p>
|
||||
<p className="text-white/40 text-xs">成交订单</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#ff3b5c] text-xl font-bold">90%</p>
|
||||
<p className="text-white/40 text-xs">佣金率</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 邀请码 */}
|
||||
<div className="mt-4 pt-4 border-t border-white/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-white/50 text-xs">我的邀请码</p>
|
||||
<p className="text-[#ff3b5c] font-mono text-lg">{user?.referralCode || "---"}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCopyCode}
|
||||
className="px-4 py-2 rounded-lg bg-[#2c2c2e] text-white text-sm flex items-center gap-2"
|
||||
>
|
||||
{copied ? <Check className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4" />}
|
||||
{copied ? "已复制" : "复制"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 菜单列表 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<Link
|
||||
href="/my/purchases"
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📦</span>
|
||||
<span className="text-white">我的订单</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/my/bookmarks"
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">🏷️</span>
|
||||
<span className="text-white">我的书签</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link href="/my/notes" className="flex items-center justify-between p-4 active:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📝</span>
|
||||
<span className="text-white">阅读笔记</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 设置菜单 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<Link
|
||||
href="/my/settings"
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">⚙️</span>
|
||||
<span className="text-white">设置</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/my/contact"
|
||||
className="flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">💬</span>
|
||||
<span className="text-white">联系客服</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
<Link href="/about" className="flex items-center justify-between p-4 active:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">ℹ️</span>
|
||||
<span className="text-white">关于我们</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 退出登录 */}
|
||||
<div className="mx-4 mt-4">
|
||||
<button
|
||||
onClick={logout}
|
||||
className="w-full py-3 rounded-xl bg-[#1c1c1e] text-[#ff3b5c] font-medium border border-white/5"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ChevronLeft, Lock, Share2, Sparkles } from "lucide-react"
|
||||
import { type Section, getFullBookPrice, getTotalSectionCount } from "@/lib/book-data"
|
||||
import { useStore } from "@/lib/store"
|
||||
import { PaymentModal } from "./payment-modal"
|
||||
import { AuthModal } from "./modules/auth/auth-modal"
|
||||
|
||||
interface ChapterContentProps {
|
||||
section: Section & { filePath: string }
|
||||
@@ -18,6 +19,7 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
const [content, setContent] = useState<string>("")
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isPaymentOpen, setIsPaymentOpen] = useState(false)
|
||||
const [isAuthOpen, setIsAuthOpen] = useState(false)
|
||||
const [paymentType, setPaymentType] = useState<"section" | "fullbook">("section")
|
||||
const [readingProgress, setReadingProgress] = useState(0)
|
||||
|
||||
@@ -68,25 +70,23 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
|
||||
const handlePurchaseClick = (type: "section" | "fullbook") => {
|
||||
if (!isLoggedIn) {
|
||||
router.push("/login")
|
||||
setIsAuthOpen(true)
|
||||
return
|
||||
}
|
||||
setPaymentType(type)
|
||||
setIsPaymentOpen(true)
|
||||
}
|
||||
|
||||
// 计算预览内容(前50%)
|
||||
const contentLines = content.split("\n").filter((line) => line.trim())
|
||||
const previewLineCount = Math.ceil(contentLines.length * 0.5)
|
||||
const previewLineCount = Math.ceil(contentLines.length * 0.1) // 改为10%
|
||||
const previewContent = contentLines.slice(0, previewLineCount).join("\n")
|
||||
const hiddenContent = contentLines.slice(previewLineCount).join("\n")
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white">
|
||||
{/* 阅读进度条 */}
|
||||
<div className="fixed top-0 left-0 right-0 z-50 h-0.5 bg-[#1c1c1e]">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-[#30d158] to-[#00c7be] transition-all duration-150"
|
||||
className="h-full bg-gradient-to-r from-[#ff3b5c] to-[#ff6b8a] transition-all duration-150"
|
||||
style={{ width: `${readingProgress}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -122,10 +122,10 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
{/* 标题 */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-[#30d158] text-sm font-medium bg-[#30d158]/10 px-3 py-1 rounded-full">
|
||||
<span className="text-[#ff3b5c] text-sm font-medium bg-[#ff3b5c]/10 px-3 py-1 rounded-full">
|
||||
{section.id}
|
||||
</span>
|
||||
{section.isFree && <span className="text-xs text-[#30d158] bg-[#30d158]/10 px-2 py-0.5 rounded">免费</span>}
|
||||
{section.isFree && <span className="text-xs text-[#00E5FF] bg-[#00E5FF]/10 px-2 py-0.5 rounded">免费</span>}
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-white leading-tight">{section.title}</h1>
|
||||
</div>
|
||||
@@ -153,7 +153,6 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
)}
|
||||
</article>
|
||||
) : (
|
||||
// 付费墙:前半免费,后半付费
|
||||
<div>
|
||||
{/* 免费预览部分 */}
|
||||
<article className="text-gray-300 leading-[1.9] text-[17px]">
|
||||
@@ -175,12 +174,12 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
{/* 付费提示卡片 */}
|
||||
<div className="mt-8 p-6 rounded-2xl bg-gradient-to-b from-[#1c1c1e] to-[#2c2c2e] border border-white/10">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-[#30d158]/10 flex items-center justify-center">
|
||||
<Lock className="w-8 h-8 text-[#30d158]" />
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-[#ff3b5c]/10 flex items-center justify-center">
|
||||
<Lock className="w-8 h-8 text-[#ff3b5c]" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-2">解锁完整内容</h3>
|
||||
<p className="text-gray-400 text-sm mb-6">
|
||||
已阅读50%,{isLoggedIn ? "购买后继续阅读" : "登录并购买后继续阅读"}
|
||||
已阅读10%,{isLoggedIn ? "购买后继续阅读" : "登录并购买后继续阅读"}
|
||||
</p>
|
||||
|
||||
{/* 购买选项 */}
|
||||
@@ -191,13 +190,13 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span>购买本章</span>
|
||||
<span className="text-[#30d158]">¥{section.price}</span>
|
||||
<span className="text-[#ff3b5c]">¥{section.price}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => handlePurchaseClick("fullbook")}
|
||||
className="w-full py-3.5 px-6 rounded-xl bg-gradient-to-r from-[#30d158] to-[#00c7be] text-white font-medium active:scale-[0.98] transition-transform shadow-lg shadow-[#30d158]/20"
|
||||
className="w-full py-3.5 px-6 rounded-xl bg-gradient-to-r from-[#ff3b5c] to-[#ff6b8a] text-white font-medium active:scale-[0.98] transition-transform shadow-lg shadow-[#ff3b5c]/20"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -219,6 +218,9 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
|
||||
)}
|
||||
</main>
|
||||
|
||||
{/* 登录弹窗 */}
|
||||
<AuthModal isOpen={isAuthOpen} onClose={() => setIsAuthOpen(false)} />
|
||||
|
||||
{/* 支付弹窗 */}
|
||||
<PaymentModal
|
||||
isOpen={isPaymentOpen}
|
||||
|
||||
47
components/layout/bottom-nav.tsx
Normal file
47
components/layout/bottom-nav.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { Home, Users, User } from "lucide-react"
|
||||
|
||||
export function BottomNav() {
|
||||
const pathname = usePathname()
|
||||
|
||||
// 管理后台不显示底部导航
|
||||
if (pathname.startsWith("/admin")) return null
|
||||
|
||||
const navItems = [
|
||||
{ href: "/", icon: Home, label: "首页", id: "home" },
|
||||
{ href: "/match", icon: Users, label: "匹配合作", id: "match" },
|
||||
{ href: "/my", icon: User, label: "我的", id: "my" },
|
||||
]
|
||||
|
||||
const isActive = (href: string) => {
|
||||
if (href === "/") return pathname === "/"
|
||||
return pathname.startsWith(href)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-[#0a0a0a]/95 backdrop-blur-xl border-t border-white/5 safe-area-bottom">
|
||||
<div className="flex items-center justify-around h-16 max-w-lg mx-auto">
|
||||
{navItems.map((item) => {
|
||||
const active = isActive(item.href)
|
||||
const Icon = item.icon
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={item.href}
|
||||
className={`flex flex-col items-center justify-center w-20 h-full transition-colors ${
|
||||
active ? "text-[#ff3b5c]" : "text-white/40"
|
||||
}`}
|
||||
>
|
||||
<Icon className={`w-6 h-6 mb-1 ${active ? "stroke-[2.5]" : ""}`} />
|
||||
<span className="text-xs font-medium">{item.label}</span>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { X, Phone, User, Gift } from "lucide-react"
|
||||
import { X, Phone, Lock, User, Gift } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { useStore } from "@/lib/store"
|
||||
@@ -15,46 +15,58 @@ interface AuthModalProps {
|
||||
export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalProps) {
|
||||
const [tab, setTab] = useState<"login" | "register">(defaultTab)
|
||||
const [phone, setPhone] = useState("")
|
||||
const [code, setCode] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
const [nickname, setNickname] = useState("")
|
||||
const [referralCode, setReferralCode] = useState("")
|
||||
const [error, setError] = useState("")
|
||||
const [codeSent, setCodeSent] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const { login, register } = useStore()
|
||||
|
||||
const handleSendCode = () => {
|
||||
const handleLogin = async () => {
|
||||
setError("")
|
||||
if (phone.length !== 11) {
|
||||
setError("请输入正确的手机号")
|
||||
return
|
||||
}
|
||||
// Simulate sending verification code
|
||||
setCodeSent(true)
|
||||
setError("")
|
||||
alert("验证码已发送,测试验证码: 123456")
|
||||
if (!password) {
|
||||
setError("请输入密码")
|
||||
return
|
||||
}
|
||||
|
||||
const handleLogin = async () => {
|
||||
setError("")
|
||||
const success = await login(phone, code)
|
||||
setIsLoading(true)
|
||||
// 使用密码替代验证码进行登录
|
||||
const success = await login(phone, password)
|
||||
setIsLoading(false)
|
||||
|
||||
if (success) {
|
||||
onClose()
|
||||
} else {
|
||||
setError("验证码错误或用户不存在,请先注册")
|
||||
setError("手机号或密码错误,请先注册")
|
||||
}
|
||||
}
|
||||
|
||||
const handleRegister = async () => {
|
||||
setError("")
|
||||
if (!nickname.trim()) {
|
||||
setError("请输入昵称")
|
||||
if (phone.length !== 11) {
|
||||
setError("请输入正确的手机号")
|
||||
return
|
||||
}
|
||||
const success = await register(phone, nickname, referralCode || undefined)
|
||||
if (!password || password.length < 6) {
|
||||
setError("密码至少6位")
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoading(true)
|
||||
// 昵称可选,默认使用手机号后四位
|
||||
const name = nickname.trim() || `用户${phone.slice(-4)}`
|
||||
const success = await register(phone, name, referralCode || undefined)
|
||||
setIsLoading(false)
|
||||
|
||||
if (success) {
|
||||
onClose()
|
||||
} else {
|
||||
setError("该手机号已注册")
|
||||
setError("该手机号已注册,请直接登录")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,32 +75,38 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
{/* Backdrop */}
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onClose} />
|
||||
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
|
||||
|
||||
{/* Modal */}
|
||||
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 overflow-hidden">
|
||||
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl border border-white/10 overflow-hidden">
|
||||
{/* Close button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 p-2 text-gray-400 hover:text-white transition-colors"
|
||||
className="absolute top-4 right-4 p-2 text-white/40 hover:text-white transition-colors"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-gray-700/50">
|
||||
<div className="flex border-b border-white/10">
|
||||
<button
|
||||
onClick={() => setTab("login")}
|
||||
onClick={() => {
|
||||
setTab("login")
|
||||
setError("")
|
||||
}}
|
||||
className={`flex-1 py-4 text-center transition-colors ${
|
||||
tab === "login" ? "text-white border-b-2 border-[#38bdac]" : "text-gray-400 hover:text-white"
|
||||
tab === "login" ? "text-white border-b-2 border-[#ff3b5c]" : "text-white/40 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTab("register")}
|
||||
onClick={() => {
|
||||
setTab("register")
|
||||
setError("")
|
||||
}}
|
||||
className={`flex-1 py-4 text-center transition-colors ${
|
||||
tab === "register" ? "text-white border-b-2 border-[#38bdac]" : "text-gray-400 hover:text-white"
|
||||
tab === "register" ? "text-white border-b-2 border-[#ff3b5c]" : "text-white/40 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
注册
|
||||
@@ -100,121 +118,113 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
{tab === "login" ? (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">手机号</label>
|
||||
<label className="block text-white/60 text-sm mb-2">手机号</label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="tel"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
placeholder="请输入手机号"
|
||||
className="pl-10 bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
maxLength={11}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">验证码</label>
|
||||
<div className="flex gap-3">
|
||||
<label className="block text-white/60 text-sm mb-2">密码</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="text"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
placeholder="请输入验证码"
|
||||
className="bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
maxLength={6}
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="请输入密码"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-[#ff3b5c] text-sm">{error}</p>}
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleSendCode}
|
||||
disabled={codeSent}
|
||||
className="whitespace-nowrap border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent"
|
||||
onClick={handleLogin}
|
||||
disabled={isLoading}
|
||||
className="w-full bg-[#ff3b5c] hover:bg-[#ff5c7a] text-white h-12 rounded-xl font-medium"
|
||||
>
|
||||
{codeSent ? "已发送" : "获取验证码"}
|
||||
{isLoading ? "登录中..." : "登录"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||
|
||||
<Button onClick={handleLogin} className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white py-5">
|
||||
登录
|
||||
</Button>
|
||||
<p className="text-center text-white/40 text-xs">测试账号:任意11位手机号,密码:123456</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">手机号</label>
|
||||
<label className="block text-white/60 text-sm mb-2">手机号</label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="tel"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
placeholder="请输入手机号"
|
||||
className="pl-10 bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
maxLength={11}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">昵称</label>
|
||||
<label className="block text-white/60 text-sm mb-2">密码</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="设置密码(至少6位)"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-white/60 text-sm mb-2">昵称(选填)</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="text"
|
||||
value={nickname}
|
||||
onChange={(e) => setNickname(e.target.value)}
|
||||
placeholder="请输入昵称"
|
||||
className="pl-10 bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
placeholder="不填则使用默认昵称"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">验证码</label>
|
||||
<div className="flex gap-3">
|
||||
<Input
|
||||
type="text"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
placeholder="请输入验证码"
|
||||
className="bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
maxLength={6}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleSendCode}
|
||||
disabled={codeSent}
|
||||
className="whitespace-nowrap border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent"
|
||||
>
|
||||
{codeSent ? "已发送" : "获取验证码"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-gray-400 text-sm mb-2">邀请码 (选填)</label>
|
||||
<label className="block text-white/60 text-sm mb-2">邀请码(选填)</label>
|
||||
<div className="relative">
|
||||
<Gift className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||||
<Gift className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
|
||||
<Input
|
||||
type="text"
|
||||
value={referralCode}
|
||||
onChange={(e) => setReferralCode(e.target.value)}
|
||||
placeholder="填写邀请码可获得优惠"
|
||||
className="pl-10 bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500"
|
||||
placeholder="填写可获得优惠"
|
||||
className="pl-10 bg-[#2c2c2e] border-white/10 text-white placeholder:text-white/30 h-12 rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||
{error && <p className="text-[#ff3b5c] text-sm">{error}</p>}
|
||||
|
||||
<Button onClick={handleRegister} className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white py-5">
|
||||
注册
|
||||
<Button
|
||||
onClick={handleRegister}
|
||||
disabled={isLoading}
|
||||
className="w-full bg-[#ff3b5c] hover:bg-[#ff5c7a] text-white h-12 rounded-xl font-medium"
|
||||
>
|
||||
{isLoading ? "注册中..." : "立即注册"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user