172 lines
6.4 KiB
TypeScript
172 lines
6.4 KiB
TypeScript
|
|
"use client"
|
|||
|
|
|
|||
|
|
import { useState, useEffect } from "react"
|
|||
|
|
import { Sparkles, Users, BookOpen } from "lucide-react"
|
|||
|
|
import { getAllSections } from "@/lib/book-data"
|
|||
|
|
import { useStore } from "@/lib/store"
|
|||
|
|
|
|||
|
|
export function MatchingCircle() {
|
|||
|
|
const [isMatching, setIsMatching] = useState(false)
|
|||
|
|
const [matchProgress, setMatchProgress] = useState(0)
|
|||
|
|
const [matchResult, setMatchResult] = useState<{
|
|||
|
|
section: { id: string; title: string }
|
|||
|
|
reason: string
|
|||
|
|
compatibility: number
|
|||
|
|
} | null>(null)
|
|||
|
|
|
|||
|
|
const { user, isLoggedIn } = useStore()
|
|||
|
|
const allSections = getAllSections()
|
|||
|
|
|
|||
|
|
// 开始匹配
|
|||
|
|
const startMatching = () => {
|
|||
|
|
if (isMatching) return
|
|||
|
|
|
|||
|
|
setIsMatching(true)
|
|||
|
|
setMatchProgress(0)
|
|||
|
|
setMatchResult(null)
|
|||
|
|
|
|||
|
|
// 模拟匹配进度
|
|||
|
|
const interval = setInterval(() => {
|
|||
|
|
setMatchProgress((prev) => {
|
|||
|
|
if (prev >= 100) {
|
|||
|
|
clearInterval(interval)
|
|||
|
|
// 匹配完成,生成结果
|
|||
|
|
const randomSection = allSections[Math.floor(Math.random() * allSections.length)]
|
|||
|
|
const reasons = [
|
|||
|
|
"与你的创业方向高度匹配",
|
|||
|
|
"适合你当前的发展阶段",
|
|||
|
|
"契合你的商业思维模式",
|
|||
|
|
"与你的行业背景相关",
|
|||
|
|
"符合你的学习偏好",
|
|||
|
|
]
|
|||
|
|
setMatchResult({
|
|||
|
|
section: { id: randomSection.id, title: randomSection.title },
|
|||
|
|
reason: reasons[Math.floor(Math.random() * reasons.length)],
|
|||
|
|
compatibility: Math.floor(Math.random() * 20) + 80,
|
|||
|
|
})
|
|||
|
|
setIsMatching(false)
|
|||
|
|
return 100
|
|||
|
|
}
|
|||
|
|
return prev + 2
|
|||
|
|
})
|
|||
|
|
}, 50)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存匹配结果到本地
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (matchResult && isLoggedIn) {
|
|||
|
|
const savedResults = JSON.parse(localStorage.getItem("match_results") || "[]")
|
|||
|
|
savedResults.unshift({
|
|||
|
|
...matchResult,
|
|||
|
|
userId: user?.id,
|
|||
|
|
matchedAt: new Date().toISOString(),
|
|||
|
|
})
|
|||
|
|
// 只保留最近10条
|
|||
|
|
localStorage.setItem("match_results", JSON.stringify(savedResults.slice(0, 10)))
|
|||
|
|
}
|
|||
|
|
}, [matchResult, isLoggedIn, user?.id])
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="w-full max-w-sm text-center">
|
|||
|
|
{/* 匹配圆环 */}
|
|||
|
|
<div className="relative w-64 h-64 mx-auto mb-8">
|
|||
|
|
{/* 外圈装饰 */}
|
|||
|
|
<div className="absolute inset-0 rounded-full border-2 border-[var(--app-border)] opacity-30" />
|
|||
|
|
<div className="absolute inset-2 rounded-full border border-[var(--app-border)] opacity-20" />
|
|||
|
|
<div className="absolute inset-4 rounded-full border border-[var(--app-border)] opacity-10" />
|
|||
|
|
|
|||
|
|
{/* 进度圆环 */}
|
|||
|
|
<svg className="absolute inset-0 w-full h-full -rotate-90">
|
|||
|
|
<circle cx="128" cy="128" r="120" fill="none" stroke="var(--app-bg-tertiary)" strokeWidth="4" />
|
|||
|
|
<circle
|
|||
|
|
cx="128"
|
|||
|
|
cy="128"
|
|||
|
|
r="120"
|
|||
|
|
fill="none"
|
|||
|
|
stroke="var(--app-brand)"
|
|||
|
|
strokeWidth="4"
|
|||
|
|
strokeLinecap="round"
|
|||
|
|
strokeDasharray={`${2 * Math.PI * 120}`}
|
|||
|
|
strokeDashoffset={`${2 * Math.PI * 120 * (1 - matchProgress / 100)}`}
|
|||
|
|
className="transition-all duration-100"
|
|||
|
|
style={{
|
|||
|
|
filter: isMatching ? "drop-shadow(0 0 10px var(--app-brand))" : "none",
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
</svg>
|
|||
|
|
|
|||
|
|
{/* 中心内容 */}
|
|||
|
|
<div className="absolute inset-8 rounded-full glass-card flex flex-col items-center justify-center">
|
|||
|
|
{isMatching ? (
|
|||
|
|
<>
|
|||
|
|
<Sparkles className="w-10 h-10 text-[var(--app-brand)] animate-pulse mb-2" />
|
|||
|
|
<p className="text-[var(--app-brand)] text-2xl font-bold">{matchProgress}%</p>
|
|||
|
|
<p className="text-[var(--app-text-tertiary)] text-xs">正在匹配...</p>
|
|||
|
|
</>
|
|||
|
|
) : matchResult ? (
|
|||
|
|
<>
|
|||
|
|
<div className="text-[var(--app-brand)] text-3xl font-bold mb-1">{matchResult.compatibility}%</div>
|
|||
|
|
<p className="text-white text-xs mb-1">匹配度</p>
|
|||
|
|
<p className="text-[var(--app-text-tertiary)] text-[10px]">{matchResult.reason}</p>
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
<Users className="w-10 h-10 text-[var(--app-text-tertiary)] mb-2" />
|
|||
|
|
<p className="text-white text-sm">寻找合作伙伴</p>
|
|||
|
|
<p className="text-[var(--app-text-tertiary)] text-xs">智能匹配商业故事</p>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 浮动装饰点 */}
|
|||
|
|
{isMatching && (
|
|||
|
|
<>
|
|||
|
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-2 h-2 rounded-full bg-[var(--app-brand)] animate-ping" />
|
|||
|
|
<div
|
|||
|
|
className="absolute bottom-0 left-1/2 -translate-x-1/2 w-2 h-2 rounded-full bg-[var(--ios-blue)] animate-ping"
|
|||
|
|
style={{ animationDelay: "0.5s" }}
|
|||
|
|
/>
|
|||
|
|
<div
|
|||
|
|
className="absolute left-0 top-1/2 -translate-y-1/2 w-2 h-2 rounded-full bg-[var(--ios-purple)] animate-ping"
|
|||
|
|
style={{ animationDelay: "0.25s" }}
|
|||
|
|
/>
|
|||
|
|
<div
|
|||
|
|
className="absolute right-0 top-1/2 -translate-y-1/2 w-2 h-2 rounded-full bg-[var(--ios-teal)] animate-ping"
|
|||
|
|
style={{ animationDelay: "0.75s" }}
|
|||
|
|
/>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 匹配结果 */}
|
|||
|
|
{matchResult && (
|
|||
|
|
<div className="glass-card p-4 mb-6 text-left">
|
|||
|
|
<div className="flex items-center gap-3">
|
|||
|
|
<div className="w-10 h-10 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center">
|
|||
|
|
<BookOpen className="w-5 h-5 text-[var(--app-brand)]" />
|
|||
|
|
</div>
|
|||
|
|
<div className="flex-1 min-w-0">
|
|||
|
|
<p className="text-[var(--app-brand)] text-xs mb-0.5">{matchResult.section.id}</p>
|
|||
|
|
<p className="text-white text-sm truncate">{matchResult.section.title}</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 匹配按钮 */}
|
|||
|
|
<button
|
|||
|
|
onClick={startMatching}
|
|||
|
|
disabled={isMatching}
|
|||
|
|
className={`btn-ios w-full flex items-center justify-center gap-2 ${
|
|||
|
|
isMatching ? "opacity-50 cursor-not-allowed" : "glow"
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<Sparkles className="w-5 h-5" />
|
|||
|
|
<span>{isMatching ? "匹配中..." : matchResult ? "重新匹配" : "开始匹配"}</span>
|
|||
|
|
</button>
|
|||
|
|
|
|||
|
|
<p className="text-[var(--app-text-tertiary)] text-xs mt-4">基于你的阅读偏好,智能推荐适合你的商业故事</p>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|