feat: implement Cunkebao API integration
Create API route and update match page for recruitment, resource linkage, and mentor consultant features #VERCEL_SKIP Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
95
app/api/cunkebao/route.ts
Normal file
95
app/api/cunkebao/route.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import crypto from "crypto"
|
||||
|
||||
// 存客宝API配置
|
||||
const CKB_CONFIG = {
|
||||
apiKey: "fyngh-ecy9h-qkdae-epwd5-rz6kd",
|
||||
apiUrl: "https://ckbapi.quwanzhi.com/v1/api/scenarios",
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
function generateSign(apiKey: string, timestamp: number): string {
|
||||
const signStr = `${apiKey}${timestamp}`
|
||||
return crypto.createHash("md5").update(signStr).digest("hex")
|
||||
}
|
||||
|
||||
// 不同场景的source和tags配置
|
||||
const SCENARIO_CONFIG: Record<string, { source: string; tags: string }> = {
|
||||
team: {
|
||||
source: "卡若创业实验-切片团队招募",
|
||||
tags: "切片团队,团队招募,创业合作",
|
||||
},
|
||||
resource: {
|
||||
source: "卡若创业实验-资源对接",
|
||||
tags: "资源对接,资源群,商业合作",
|
||||
},
|
||||
mentor: {
|
||||
source: "卡若创业实验-导师顾问",
|
||||
tags: "导师顾问,创业指导,商业咨询",
|
||||
},
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { phone, name, scenario, remark } = body
|
||||
|
||||
// 验证必填参数
|
||||
if (!phone) {
|
||||
return NextResponse.json({ success: false, message: "手机号不能为空" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!scenario || !SCENARIO_CONFIG[scenario]) {
|
||||
return NextResponse.json({ success: false, message: "无效的场景类型" }, { status: 400 })
|
||||
}
|
||||
|
||||
// 生成时间戳和签名
|
||||
const timestamp = Math.floor(Date.now() / 1000)
|
||||
const sign = generateSign(CKB_CONFIG.apiKey, timestamp)
|
||||
|
||||
// 获取场景配置
|
||||
const config = SCENARIO_CONFIG[scenario]
|
||||
|
||||
// 构建请求体
|
||||
const requestBody = {
|
||||
apiKey: CKB_CONFIG.apiKey,
|
||||
sign,
|
||||
timestamp,
|
||||
phone,
|
||||
name: name || "",
|
||||
source: config.source,
|
||||
remark: remark || `来自${config.source}`,
|
||||
tags: config.tags,
|
||||
}
|
||||
|
||||
// 调用存客宝API
|
||||
const response = await fetch(CKB_CONFIG.apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
if (response.ok && result.code === 0) {
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: "提交成功,我们会尽快与您联系",
|
||||
data: result.data,
|
||||
})
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
message: result.message || "提交失败,请稍后重试",
|
||||
},
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("存客宝API调用失败:", error)
|
||||
return NextResponse.json({ success: false, message: "服务器错误,请稍后重试" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from "react"
|
||||
import { motion, AnimatePresence } from "framer-motion"
|
||||
import { Mic } from "lucide-react"
|
||||
import { Mic, X } from "lucide-react"
|
||||
import { Home, List, User } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
@@ -18,18 +18,206 @@ interface MatchUser {
|
||||
}
|
||||
|
||||
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" },
|
||||
{ id: "team", label: "团队招募", icon: "🎮", color: "#4CAF50", scenario: "team" },
|
||||
{ id: "resource", label: "资源对接", icon: "👥", color: "#7B61FF", scenario: "resource" },
|
||||
{ id: "mentor", label: "导师顾问", icon: "❤️", color: "#E91E63", scenario: "mentor" },
|
||||
]
|
||||
|
||||
interface JoinModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
type: (typeof matchTypes)[0]
|
||||
}
|
||||
|
||||
function JoinModal({ isOpen, onClose, type }: JoinModalProps) {
|
||||
const [phone, setPhone] = useState("")
|
||||
const [name, setName] = useState("")
|
||||
const [remark, setRemark] = useState("")
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [submitResult, setSubmitResult] = useState<{ success: boolean; message: string } | null>(null)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!phone) {
|
||||
setSubmitResult({ success: false, message: "请输入手机号" })
|
||||
return
|
||||
}
|
||||
|
||||
// 简单的手机号验证
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
setSubmitResult({ success: false, message: "请输入正确的手机号" })
|
||||
return
|
||||
}
|
||||
|
||||
setIsSubmitting(true)
|
||||
setSubmitResult(null)
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/cunkebao", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
phone,
|
||||
name,
|
||||
scenario: type.scenario,
|
||||
remark,
|
||||
}),
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
setSubmitResult(result)
|
||||
|
||||
if (result.success) {
|
||||
// 成功后清空表单
|
||||
setPhone("")
|
||||
setName("")
|
||||
setRemark("")
|
||||
// 3秒后关闭弹窗
|
||||
setTimeout(() => {
|
||||
onClose()
|
||||
setSubmitResult(null)
|
||||
}, 3000)
|
||||
}
|
||||
} catch (error) {
|
||||
setSubmitResult({ success: false, message: "网络错误,请稍后重试" })
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
const getTitle = () => {
|
||||
switch (type.id) {
|
||||
case "team":
|
||||
return "加入切片团队"
|
||||
case "resource":
|
||||
return "加入资源群"
|
||||
case "mentor":
|
||||
return "预约导师顾问"
|
||||
default:
|
||||
return "加入"
|
||||
}
|
||||
}
|
||||
|
||||
const getDescription = () => {
|
||||
switch (type.id) {
|
||||
case "team":
|
||||
return "加入切片团队,一起创造价值,共享收益"
|
||||
case "resource":
|
||||
return "加入资源对接群,链接优质商业资源"
|
||||
case "mentor":
|
||||
return "预约一对一导师咨询,获取专业指导"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4"
|
||||
onClick={onClose}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
className="bg-[#1c1c1e] rounded-2xl w-full max-w-sm overflow-hidden"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* 头部 */}
|
||||
<div className="relative p-6 pb-4">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute right-4 top-4 w-8 h-8 rounded-full bg-white/10 flex items-center justify-center"
|
||||
>
|
||||
<X className="w-4 h-4 text-white/60" />
|
||||
</button>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-3xl">{type.icon}</span>
|
||||
<h2 className="text-xl font-bold text-white">{getTitle()}</h2>
|
||||
</div>
|
||||
<p className="text-white/50 text-sm">{getDescription()}</p>
|
||||
</div>
|
||||
|
||||
{/* 表单 */}
|
||||
<div className="px-6 pb-6 space-y-4">
|
||||
<div>
|
||||
<label className="block text-white/60 text-sm mb-2">手机号 *</label>
|
||||
<input
|
||||
type="tel"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
placeholder="请输入手机号"
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:border-[#00E5FF]/50 focus:outline-none transition-colors"
|
||||
maxLength={11}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-white/60 text-sm mb-2">姓名(选填)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="请输入您的姓名"
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:border-[#00E5FF]/50 focus:outline-none transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-white/60 text-sm mb-2">备注(选填)</label>
|
||||
<textarea
|
||||
value={remark}
|
||||
onChange={(e) => setRemark(e.target.value)}
|
||||
placeholder="简单介绍一下您自己"
|
||||
rows={3}
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:border-[#00E5FF]/50 focus:outline-none transition-colors resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 提交结果提示 */}
|
||||
{submitResult && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className={`p-3 rounded-xl text-sm ${
|
||||
submitResult.success ? "bg-green-500/20 text-green-400" : "bg-red-500/20 text-red-400"
|
||||
}`}
|
||||
>
|
||||
{submitResult.message}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 提交按钮 */}
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting}
|
||||
className="w-full py-4 rounded-xl bg-[#00E5FF] text-black font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-opacity"
|
||||
>
|
||||
{isSubmitting ? "提交中..." : "立即加入"}
|
||||
</button>
|
||||
|
||||
<p className="text-white/30 text-xs text-center">提交后我们会尽快与您联系</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
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 [selectedType, setSelectedType] = useState(matchTypes[0])
|
||||
const [showJoinModal, setShowJoinModal] = useState(false)
|
||||
const [joinType, setJoinType] = useState(matchTypes[0])
|
||||
const router = useRouter()
|
||||
|
||||
const startMatch = () => {
|
||||
@@ -65,7 +253,7 @@ export default function MatchPage() {
|
||||
id: `user_${Date.now()}`,
|
||||
nickname: nicknames[randomIndex],
|
||||
avatar: `https://picsum.photos/200/200?random=${randomIndex}`,
|
||||
tags: ["创业者", "私域运营", matchTypes.find((t) => t.id === selectedType)?.label || ""],
|
||||
tags: ["创业者", "私域运营", selectedType.label],
|
||||
matchScore: Math.floor(Math.random() * 20) + 80,
|
||||
concept: concepts[randomIndex % concepts.length],
|
||||
wechat: wechats[randomIndex % wechats.length],
|
||||
@@ -94,13 +282,15 @@ export default function MatchPage() {
|
||||
})
|
||||
}
|
||||
|
||||
const currentTypeLabel = matchTypes.find((t) => t.id === selectedType)?.label || "创业合伙"
|
||||
const handleTypeClick = (type: (typeof matchTypes)[0]) => {
|
||||
setJoinType(type)
|
||||
setShowJoinModal(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<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>
|
||||
<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
|
||||
@@ -113,34 +303,6 @@ export default function MatchPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 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>
|
||||
|
||||
<AnimatePresence mode="wait">
|
||||
{!isMatching && !currentMatch && (
|
||||
<motion.div
|
||||
@@ -215,35 +377,28 @@ export default function MatchPage() {
|
||||
{/* 中心图标 */}
|
||||
<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>
|
||||
<div className="text-sm text-white/60 relative z-10">寻找{selectedType.label}</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{/* 当前模式显示 */}
|
||||
<p className="text-white/50 text-sm mb-8">
|
||||
当前模式: <span className="text-[#00E5FF]">{currentTypeLabel}</span>
|
||||
当前模式: <span className="text-[#00E5FF]">{selectedType.label}</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">
|
||||
<div className="grid grid-cols-3 gap-4 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"
|
||||
}`}
|
||||
onClick={() => handleTypeClick(type)}
|
||||
className="p-5 rounded-xl flex flex-col items-center gap-3 transition-all bg-[#1c1c1e] border border-white/10 hover:border-[#00E5FF]/50 hover:bg-[#00E5FF]/5 active:scale-95"
|
||||
>
|
||||
<span className="text-2xl">{type.icon}</span>
|
||||
<span className={`text-xs ${selectedType === type.id ? "text-[#00E5FF]" : "text-white/60"}`}>
|
||||
{type.label}
|
||||
</span>
|
||||
<span className="text-3xl">{type.icon}</span>
|
||||
<span className="text-sm text-white/80">{type.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -369,20 +524,21 @@ export default function MatchPage() {
|
||||
{/* 操作按钮 */}
|
||||
<div className="space-y-3">
|
||||
<button onClick={handleAddWechat} className="w-full py-4 rounded-xl bg-[#00E5FF] text-black font-medium">
|
||||
➕ 一键加好友
|
||||
一键加好友
|
||||
</button>
|
||||
<button
|
||||
onClick={nextMatch}
|
||||
className="w-full py-4 rounded-xl bg-[#1c1c1e] text-white border border-white/10"
|
||||
>
|
||||
🔄 重新匹配
|
||||
重新匹配
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* 底部导航 */}
|
||||
<JoinModal isOpen={showJoinModal} onClose={() => setShowJoinModal(false)} type={joinType} />
|
||||
|
||||
<nav className="fixed bottom-0 left-0 right-0 bg-[#1c1c1e]/95 backdrop-blur-xl border-t border-white/5 pb-safe-bottom">
|
||||
<div className="px-4 py-2">
|
||||
<div className="flex items-center justify-around">
|
||||
@@ -394,10 +550,23 @@ export default function MatchPage() {
|
||||
<List className="w-5 h-5 text-gray-500 mb-1" />
|
||||
<span className="text-gray-500 text-xs">目录</span>
|
||||
</button>
|
||||
{/* 匹配按钮 - 当前页面高亮 */}
|
||||
{/* 匹配按钮 - 当前页面高亮,小星球图标 */}
|
||||
<button className="flex flex-col items-center py-2 px-6 -mt-4">
|
||||
<div className="w-14 h-14 rounded-full bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center shadow-lg shadow-[#00CED1]/30">
|
||||
<Mic className="w-6 h-6 text-white" />
|
||||
<svg className="w-7 h-7 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8" fill="currentColor" opacity="0.9" />
|
||||
<ellipse
|
||||
cx="12"
|
||||
cy="12"
|
||||
rx="11"
|
||||
ry="4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
opacity="0.6"
|
||||
/>
|
||||
<circle cx="9" cy="10" r="1.5" fill="white" opacity="0.4" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-[#00CED1] text-xs font-medium mt-1">匹配</span>
|
||||
</button>
|
||||
|
||||
58
app/page.tsx
58
app/page.tsx
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { Search, ChevronRight, BookOpen, Clock, TrendingUp, Users, Home, List, Sparkles, User } from "lucide-react"
|
||||
import { Search, ChevronRight, BookOpen, Home, List, User } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
import { bookData, getTotalSectionCount } from "@/lib/book-data"
|
||||
|
||||
@@ -170,43 +170,30 @@ export default function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 五篇概览 */}
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-white mb-3">内容概览</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{bookData.slice(0, 4).map((part) => (
|
||||
<div className="space-y-3">
|
||||
{bookData.map((part) => (
|
||||
<div
|
||||
key={part.id}
|
||||
onClick={() => router.push("/chapters")}
|
||||
className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 cursor-pointer"
|
||||
className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 cursor-pointer active:scale-[0.98] transition-transform"
|
||||
>
|
||||
<span className="text-[#00CED1] text-xs font-medium mb-1 block">第{part.number}篇</span>
|
||||
<h4 className="text-white font-medium text-sm mb-1">{part.title}</h4>
|
||||
<p className="text-gray-500 text-xs">{part.subtitle}</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-[#00CED1]/20 to-[#20B2AA]/10 flex items-center justify-center shrink-0">
|
||||
<span className="text-[#00CED1] font-bold text-sm">{part.number}</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-white font-medium text-sm mb-0.5">{part.title}</h4>
|
||||
<p className="text-gray-500 text-xs truncate">{part.subtitle}</p>
|
||||
</div>
|
||||
<ChevronRight className="w-4 h-4 text-gray-600 shrink-0" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 数据统计 */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 text-center">
|
||||
<TrendingUp className="w-5 h-5 text-[#00CED1] mx-auto mb-2" />
|
||||
<p className="text-white font-bold">3000万</p>
|
||||
<p className="text-gray-500 text-xs">最高年流水</p>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 text-center">
|
||||
<Users className="w-5 h-5 text-[#00CED1] mx-auto mb-2" />
|
||||
<p className="text-white font-bold">55+</p>
|
||||
<p className="text-gray-500 text-xs">真实案例</p>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 text-center">
|
||||
<Clock className="w-5 h-5 text-[#00CED1] mx-auto mb-2" />
|
||||
<p className="text-white font-bold">15年</p>
|
||||
<p className="text-gray-500 text-xs">创业经验</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 序言入口 */}
|
||||
<div
|
||||
onClick={() => router.push("/read/preface")}
|
||||
@@ -233,10 +220,23 @@ export default function HomePage() {
|
||||
<List className="w-5 h-5 text-gray-500 mb-1" />
|
||||
<span className="text-gray-500 text-xs">目录</span>
|
||||
</button>
|
||||
{/* 匹配按钮 - 更大更突出 */}
|
||||
{/* 匹配按钮 - 小星球图标 */}
|
||||
<button onClick={() => router.push("/match")} className="flex flex-col items-center py-2 px-6 -mt-4">
|
||||
<div className="w-14 h-14 rounded-full bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center shadow-lg shadow-[#00CED1]/30">
|
||||
<Sparkles className="w-6 h-6 text-white" />
|
||||
<svg className="w-7 h-7 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8" fill="currentColor" opacity="0.9" />
|
||||
<ellipse
|
||||
cx="12"
|
||||
cy="12"
|
||||
rx="11"
|
||||
ry="4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
opacity="0.6"
|
||||
/>
|
||||
<circle cx="9" cy="10" r="1.5" fill="white" opacity="0.4" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-[#00CED1] text-xs mt-1">匹配</span>
|
||||
</button>
|
||||
|
||||
@@ -2,7 +2,17 @@
|
||||
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { Home, List, Sparkles, User } from "lucide-react"
|
||||
import { Home, List, User } from "lucide-react"
|
||||
|
||||
function PlanetIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8" fill="currentColor" opacity="0.9" />
|
||||
<ellipse cx="12" cy="12" rx="11" ry="4" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.6" />
|
||||
<circle cx="9" cy="10" r="1.5" fill="white" opacity="0.4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function BottomNav() {
|
||||
const pathname = usePathname()
|
||||
@@ -20,7 +30,7 @@ export function BottomNav() {
|
||||
const navItems = [
|
||||
{ href: "/", icon: Home, label: "首页" },
|
||||
{ href: "/chapters", icon: List, label: "目录" },
|
||||
{ href: "/match", icon: Sparkles, label: "匹配", isCenter: true },
|
||||
{ href: "/match", icon: PlanetIcon, label: "匹配", isCenter: true },
|
||||
{ href: "/my", icon: User, label: "我的" },
|
||||
]
|
||||
|
||||
@@ -32,7 +42,7 @@ export function BottomNav() {
|
||||
const isActive = pathname === item.href
|
||||
const Icon = item.icon
|
||||
|
||||
// 中间的匹配按钮特殊处理
|
||||
// 中间的匹配按钮特殊处理 - 使用小星球图标
|
||||
if (item.isCenter) {
|
||||
return (
|
||||
<Link key={index} href={item.href} className="flex flex-col items-center py-2 px-6 -mt-4">
|
||||
@@ -43,7 +53,7 @@ export function BottomNav() {
|
||||
: "bg-gradient-to-br from-[#00CED1] to-[#20B2AA] shadow-[#00CED1]/30"
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-6 h-6 text-white" />
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<span className={`text-xs mt-1 ${isActive ? "text-[#00CED1] font-medium" : "text-gray-500"}`}>
|
||||
{item.label}
|
||||
|
||||
Reference in New Issue
Block a user