Files
soul/components/match-modal.tsx
v0 f3195d9331 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>
2026-01-14 05:10:32 +00:00

216 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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>
)
}