refactor: overhaul homepage and app structure
Simplify homepage, show chapter counts, display directory, trim bottom nav, in-page match feature, move marketing content, and enhance "My" page. #VERCEL_SKIP Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
This commit is contained in:
@@ -2,60 +2,56 @@
|
||||
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { Home, User } from "lucide-react"
|
||||
import { Home, User, Handshake } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
import { MatchModal } from "@/components/match-modal"
|
||||
|
||||
export function BottomNav() {
|
||||
const pathname = usePathname()
|
||||
const [showMatch, setShowMatch] = useState(false)
|
||||
|
||||
// 在文档页面和管理后台不显示底部导航
|
||||
if (pathname.startsWith("/documentation") || pathname.startsWith("/admin")) {
|
||||
// 在管理后台不显示底部导航
|
||||
if (pathname.startsWith("/admin")) {
|
||||
return null
|
||||
}
|
||||
|
||||
const navItems = [
|
||||
{ href: "/", icon: Home, label: "首页" },
|
||||
{ href: "/match", emoji: "🤝", label: "匹配合作" },
|
||||
{ href: "/my", icon: User, label: "我的" },
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* iOS风格底部导航 - 只有3个按钮 */}
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-40 glass-nav safe-bottom">
|
||||
{/* iOS风格底部导航 - 只有3个按钮,匹配在当前页面弹窗 */}
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-40 bg-black/80 backdrop-blur-xl border-t border-white/[0.06] safe-bottom">
|
||||
<div className="flex items-center justify-around py-2 max-w-lg mx-auto">
|
||||
{navItems.map((item, index) => {
|
||||
const isActive = pathname === item.href || (item.href === '/match' && pathname.startsWith('/match'))
|
||||
const Icon = item.icon
|
||||
{/* 首页 */}
|
||||
<Link href="/" className="flex flex-col items-center py-2 px-6 transition-all">
|
||||
<Home
|
||||
className={`w-5 h-5 mb-0.5 ${pathname === "/" ? "text-[var(--app-brand)]" : "text-white/40"}`}
|
||||
strokeWidth={pathname === "/" ? 2.5 : 1.5}
|
||||
/>
|
||||
<span className={`text-[10px] ${pathname === "/" ? "text-[var(--app-brand)]" : "text-white/40"}`}>
|
||||
首页
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={index}
|
||||
href={item.href!}
|
||||
className="flex flex-col items-center py-2 px-4 sm:px-6 touch-feedback transition-all duration-200"
|
||||
>
|
||||
<div className={`w-7 h-7 flex items-center justify-center mb-1 transition-colors ${
|
||||
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
|
||||
}`}>
|
||||
{item.emoji ? (
|
||||
<span className="text-2xl">{item.emoji}</span>
|
||||
) : (
|
||||
<Icon className="w-6 h-6" strokeWidth={isActive ? 2.5 : 1.5} />
|
||||
)}
|
||||
</div>
|
||||
<span className={`text-[10px] font-medium transition-colors ${
|
||||
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
|
||||
}`}>
|
||||
{item.label}
|
||||
</span>
|
||||
{/* 激活指示器 */}
|
||||
{isActive && (
|
||||
<div className="absolute -bottom-0.5 w-1 h-1 rounded-full bg-[var(--app-brand)]" />
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
{/* 匹配合作 - 点击弹出弹窗而不是跳转 */}
|
||||
<button onClick={() => setShowMatch(true)} className="flex flex-col items-center py-2 px-6 transition-all">
|
||||
<Handshake className="w-5 h-5 mb-0.5 text-white/40" strokeWidth={1.5} />
|
||||
<span className="text-[10px] text-white/40">匹配</span>
|
||||
</button>
|
||||
|
||||
{/* 我的 */}
|
||||
<Link href="/my" className="flex flex-col items-center py-2 px-6 transition-all">
|
||||
<User
|
||||
className={`w-5 h-5 mb-0.5 ${pathname.startsWith("/my") ? "text-[var(--app-brand)]" : "text-white/40"}`}
|
||||
strokeWidth={pathname.startsWith("/my") ? 2.5 : 1.5}
|
||||
/>
|
||||
<span className={`text-[10px] ${pathname.startsWith("/my") ? "text-[var(--app-brand)]" : "text-white/40"}`}>
|
||||
我的
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* 匹配弹窗 */}
|
||||
<MatchModal isOpen={showMatch} onClose={() => setShowMatch(false)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
215
components/match-modal.tsx
Normal file
215
components/match-modal.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { X, Copy, Check, RefreshCw } from "lucide-react"
|
||||
import { motion, AnimatePresence } from "framer-motion"
|
||||
|
||||
interface MatchUser {
|
||||
id: string
|
||||
nickname: string
|
||||
tags: string[]
|
||||
matchScore: number
|
||||
concept: string
|
||||
wechat: string
|
||||
}
|
||||
|
||||
interface MatchModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function MatchModal({ isOpen, onClose }: MatchModalProps) {
|
||||
const [isMatching, setIsMatching] = useState(false)
|
||||
const [currentMatch, setCurrentMatch] = useState<MatchUser | null>(null)
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const getMockMatch = (): MatchUser => {
|
||||
const data = [
|
||||
{ nickname: "创业小白", tags: ["电商", "私域"], concept: "想找供应链合作伙伴", wechat: "soul_biz_001" },
|
||||
{ nickname: "私域达人", tags: ["内容", "直播"], concept: "擅长内容运营,找技术合伙人", wechat: "soul_biz_002" },
|
||||
{ nickname: "供应链老兵", tags: ["供应链", "跨境"], concept: "有工厂资源,找流量渠道", wechat: "soul_biz_003" },
|
||||
{ nickname: "技术宅", tags: ["AI", "开发"], concept: "会写代码,想找商业方向", wechat: "soul_biz_004" },
|
||||
]
|
||||
const item = data[Math.floor(Math.random() * data.length)]
|
||||
return {
|
||||
id: `user_${Date.now()}`,
|
||||
...item,
|
||||
matchScore: Math.floor(Math.random() * 20) + 80,
|
||||
}
|
||||
}
|
||||
|
||||
const startMatch = () => {
|
||||
setIsMatching(true)
|
||||
setCurrentMatch(null)
|
||||
setCopied(false)
|
||||
|
||||
setTimeout(() => {
|
||||
setIsMatching(false)
|
||||
setCurrentMatch(getMockMatch())
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const copyWechat = () => {
|
||||
if (currentMatch) {
|
||||
navigator.clipboard.writeText(currentMatch.wechat)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setCurrentMatch(null)
|
||||
setIsMatching(false)
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
{/* 背景遮罩 */}
|
||||
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
|
||||
|
||||
{/* 弹窗内容 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
className="relative w-full max-w-sm bg-[#111] rounded-3xl overflow-hidden border border-white/10"
|
||||
>
|
||||
{/* 关闭按钮 */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 w-8 h-8 rounded-full bg-white/10 flex items-center justify-center z-10"
|
||||
>
|
||||
<X className="w-4 h-4 text-white/60" />
|
||||
</button>
|
||||
|
||||
<div className="p-6 pt-12">
|
||||
<AnimatePresence mode="wait">
|
||||
{/* 初始状态 - 开始匹配 */}
|
||||
{!isMatching && !currentMatch && (
|
||||
<motion.div
|
||||
key="idle"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="text-center"
|
||||
>
|
||||
<h2 className="text-xl font-bold text-white mb-2">寻找合作伙伴</h2>
|
||||
<p className="text-white/50 text-sm mb-8">找到志同道合的创业伙伴</p>
|
||||
|
||||
{/* 匹配按钮 - 美化的圆形按钮 */}
|
||||
<button onClick={startMatch} className="relative w-40 h-40 mx-auto mb-8 group">
|
||||
{/* 外圈光环 */}
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-[var(--app-brand)]/20 to-purple-500/20 animate-pulse" />
|
||||
<div className="absolute inset-2 rounded-full bg-gradient-to-br from-[var(--app-brand)]/30 to-purple-500/30" />
|
||||
|
||||
{/* 主按钮 */}
|
||||
<div className="absolute inset-4 rounded-full bg-gradient-to-br from-[var(--app-brand)] to-purple-500 flex flex-col items-center justify-center shadow-lg shadow-[var(--app-brand)]/30 group-active:scale-95 transition-transform">
|
||||
<span className="text-4xl mb-1">🤝</span>
|
||||
<span className="text-white font-medium text-sm">开始匹配</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className="space-y-2 text-left">
|
||||
<div className="flex items-center gap-2 text-white/50 text-xs">
|
||||
<span>💼</span>
|
||||
<span>基于创业方向智能匹配</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-white/50 text-xs">
|
||||
<span>🔒</span>
|
||||
<span>信息安全,双向确认才能联系</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 匹配中 */}
|
||||
{isMatching && (
|
||||
<motion.div
|
||||
key="matching"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="text-center py-12"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 2, repeat: Number.POSITIVE_INFINITY, ease: "linear" }}
|
||||
className="w-20 h-20 mx-auto mb-6 rounded-full border-4 border-[var(--app-brand)]/30 border-t-[var(--app-brand)]"
|
||||
/>
|
||||
<p className="text-white font-medium">正在匹配中...</p>
|
||||
<p className="text-white/40 text-sm mt-1">寻找最佳合作伙伴</p>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 匹配成功 */}
|
||||
{currentMatch && !isMatching && (
|
||||
<motion.div
|
||||
key="matched"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div className="text-center mb-4">
|
||||
<span className="text-3xl">✨</span>
|
||||
<h3 className="text-lg font-bold text-white mt-2">匹配成功!</h3>
|
||||
</div>
|
||||
|
||||
{/* 用户卡片 */}
|
||||
<div className="bg-white/5 rounded-2xl p-4 mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div>
|
||||
<h4 className="text-white font-medium">{currentMatch.nickname}</h4>
|
||||
<div className="flex gap-1 mt-1">
|
||||
{currentMatch.tags.map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="px-2 py-0.5 rounded-full text-[10px] bg-[var(--app-brand)]/20 text-[var(--app-brand)]"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<span className="text-2xl font-bold text-[var(--app-brand)]">{currentMatch.matchScore}%</span>
|
||||
<p className="text-white/40 text-[10px]">匹配度</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-white/60 text-sm">{currentMatch.concept}</p>
|
||||
</div>
|
||||
|
||||
{/* 微信号 */}
|
||||
<div className="bg-white/5 rounded-xl p-3 mb-4">
|
||||
<p className="text-white/40 text-xs mb-1">微信号</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<code className="text-[var(--app-brand)] font-mono">{currentMatch.wechat}</code>
|
||||
<button
|
||||
onClick={copyWechat}
|
||||
className="flex items-center gap-1 px-3 py-1.5 rounded-lg bg-[var(--app-brand)] text-white text-xs"
|
||||
>
|
||||
{copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
|
||||
{copied ? "已复制" : "复制"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 重新匹配 */}
|
||||
<button
|
||||
onClick={startMatch}
|
||||
className="w-full py-3 rounded-xl bg-white/5 text-white/60 text-sm flex items-center justify-center gap-2"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
换一个
|
||||
</button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user