feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API
主要更新: 1. 按H5网页端完全重构匹配功能(match页面) - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募 - 资源对接等类型弹出手机号/微信号输入框 - 去掉重新匹配按钮,改为返回按钮 2. 修复所有卡片对齐和宽度问题 - 目录页附录卡片居中 - 首页阅读进度卡片满宽度 - 我的页面菜单卡片对齐 - 推广中心分享卡片统一宽度 3. 修复目录页图标和文字对齐 - section-icon固定40rpx宽高 - section-title与图标垂直居中 4. 更新真实完整文章标题(62篇) - 从book目录读取真实markdown文件名 - 替换之前的简化标题 5. 新增文章数据API - /api/db/chapters - 获取完整书籍结构 - 支持按ID获取单篇文章内容
This commit is contained in:
259
app/my/settings/page.tsx
Normal file
259
app/my/settings/page.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Phone, MessageCircle, CreditCard, Check, X, Loader2, Shield } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter()
|
||||
const { user, updateUser, logout } = useStore()
|
||||
|
||||
// 绑定弹窗状态
|
||||
const [showBindModal, setShowBindModal] = useState(false)
|
||||
const [bindType, setBindType] = useState<"phone" | "wechat" | "alipay">("phone")
|
||||
const [bindValue, setBindValue] = useState("")
|
||||
const [isBinding, setIsBinding] = useState(false)
|
||||
const [bindError, setBindError] = useState("")
|
||||
|
||||
// 绑定账号
|
||||
const handleBind = async () => {
|
||||
if (!bindValue.trim()) {
|
||||
setBindError("请输入内容")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "phone" && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的手机号")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "wechat" && bindValue.length < 6) {
|
||||
setBindError("微信号至少6位")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "alipay" && !bindValue.includes("@") && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的支付宝账号")
|
||||
return
|
||||
}
|
||||
|
||||
setIsBinding(true)
|
||||
setBindError("")
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
if (updateUser && user) {
|
||||
const updates: any = {}
|
||||
if (bindType === "phone") updates.phone = bindValue
|
||||
if (bindType === "wechat") updates.wechat = bindValue
|
||||
if (bindType === "alipay") updates.alipay = bindValue
|
||||
updateUser(user.id, updates)
|
||||
}
|
||||
|
||||
setShowBindModal(false)
|
||||
setBindValue("")
|
||||
alert("绑定成功!")
|
||||
} catch (error) {
|
||||
setBindError("绑定失败,请重试")
|
||||
} finally {
|
||||
setIsBinding(false)
|
||||
}
|
||||
}
|
||||
|
||||
const openBindModal = (type: "phone" | "wechat" | "alipay") => {
|
||||
setBindType(type)
|
||||
setBindValue("")
|
||||
setBindError("")
|
||||
setShowBindModal(true)
|
||||
}
|
||||
|
||||
// 检查是否有绑定任何支付方式
|
||||
const hasAnyPaymentBound = user?.wechat || user?.alipay
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white pb-24">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 bg-black/90 backdrop-blur-xl border-b border-white/5">
|
||||
<div className="px-4 py-3 flex items-center">
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
<h1 className="flex-1 text-center text-lg font-semibold text-white">设置</h1>
|
||||
<div className="w-8" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="px-4 py-4 space-y-4">
|
||||
{/* 账号绑定 */}
|
||||
<div className="rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-white/5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-4 h-4 text-[#00CED1]" />
|
||||
<span className="text-white font-medium">账号绑定</span>
|
||||
</div>
|
||||
<p className="text-white/40 text-xs mt-1">绑定后可用于提现和找伙伴功能</p>
|
||||
</div>
|
||||
|
||||
{/* 手机号 */}
|
||||
<button
|
||||
onClick={() => openBindModal("phone")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.phone ? "bg-[#00CED1]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<Phone className={`w-4 h-4 ${user?.phone ? "text-[#00CED1]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">手机号</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.phone || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.phone ? (
|
||||
<Check className="w-5 h-5 text-[#00CED1]" />
|
||||
) : (
|
||||
<span className="text-[#00CED1] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 微信号 */}
|
||||
<button
|
||||
onClick={() => openBindModal("wechat")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.wechat ? "bg-[#07C160]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<MessageCircle className={`w-4 h-4 ${user?.wechat ? "text-[#07C160]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">微信号</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.wechat || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.wechat ? (
|
||||
<Check className="w-5 h-5 text-[#07C160]" />
|
||||
) : (
|
||||
<span className="text-[#07C160] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 支付宝 */}
|
||||
<button
|
||||
onClick={() => openBindModal("alipay")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.alipay ? "bg-[#1677FF]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<CreditCard className={`w-4 h-4 ${user?.alipay ? "text-[#1677FF]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">支付宝</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.alipay || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.alipay ? (
|
||||
<Check className="w-5 h-5 text-[#1677FF]" />
|
||||
) : (
|
||||
<span className="text-[#1677FF] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 绑定提示 */}
|
||||
{!hasAnyPaymentBound && (
|
||||
<div className="p-4 rounded-xl bg-orange-500/10 border border-orange-500/20">
|
||||
<p className="text-orange-400 text-xs">
|
||||
提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 退出登录 */}
|
||||
<button
|
||||
onClick={() => {
|
||||
logout()
|
||||
router.push("/")
|
||||
}}
|
||||
className="w-full py-3 rounded-xl bg-[#1c1c1e] text-red-400 font-medium border border-red-400/30"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</main>
|
||||
|
||||
{/* 绑定弹窗 */}
|
||||
{showBindModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => !isBinding && setShowBindModal(false)} />
|
||||
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl overflow-hidden">
|
||||
<div className="flex items-center justify-between p-4 border-b border-white/10">
|
||||
<h3 className="text-lg font-semibold text-white">
|
||||
绑定{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝"}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => !isBinding && setShowBindModal(false)}
|
||||
className="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>
|
||||
|
||||
<div className="p-5">
|
||||
<div className="mb-4">
|
||||
<label className="block text-white/40 text-xs mb-2">
|
||||
{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝账号"}
|
||||
</label>
|
||||
<input
|
||||
type={bindType === "phone" ? "tel" : "text"}
|
||||
value={bindValue}
|
||||
onChange={(e) => setBindValue(e.target.value)}
|
||||
placeholder={
|
||||
bindType === "phone" ? "请输入11位手机号" :
|
||||
bindType === "wechat" ? "请输入微信号" :
|
||||
"请输入支付宝账号"
|
||||
}
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:outline-none focus:border-[#00CED1]/50"
|
||||
disabled={isBinding}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{bindError && (
|
||||
<p className="text-red-400 text-sm mb-4">{bindError}</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleBind}
|
||||
disabled={isBinding || !bindValue}
|
||||
className="w-full py-3 rounded-xl bg-[#00CED1] text-black font-medium flex items-center justify-center gap-2 disabled:opacity-50"
|
||||
>
|
||||
{isBinding ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
绑定中...
|
||||
</>
|
||||
) : (
|
||||
"确认绑定"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user