"use client"; import { useState, useRef, useEffect, type FormEvent, type ChangeEvent } from "react"; import { Link } from "@/i18n/navigation"; import { getPayEnv, getRecommendedChannel, mustUseWxPay, type PayChannel, type PayEnv } from "../../lib/payEnv"; const genderOptions = ["男", "女"]; const singleOptions = ["单身", "恋爱中", "已婚"]; const educationOptions = ["高中及以下", "大专", "本科", "硕士", "博士"]; const graduationYears = Array.from({ length: 41 }, (_, i) => String(2026 - i)); interface FormData { nickname: string; gender: string; single: string; profession: string; intro: string; wechat: string; education: string; graduationYear: string; phone: string; } interface MediaInfo { preview: string; url: string; type: "image" | "video"; } export default function JoinPage() { const [form, setForm] = useState({ nickname: "", gender: "", single: "", profession: "", intro: "", wechat: "", education: "", graduationYear: "", phone: "", }); const [media, setMedia] = useState(null); const [uploading, setUploading] = useState(false); const fileRef = useRef(null); const [submitted, setSubmitted] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [payRedirecting, setPayRedirecting] = useState(false); const [payEnv, setPayEnv] = useState(null); const [payChannel, setPayChannel] = useState("alipay"); useEffect(() => { if (typeof window !== "undefined" && new URLSearchParams(window.location.search).get("paid") === "1") { setSubmitted(true); } }, []); useEffect(() => { if (typeof window === "undefined") return; const env = getPayEnv(); setPayEnv(env); setPayChannel(getRecommendedChannel(env)); }, []); const set = (key: keyof FormData, value: string) => setForm((prev) => ({ ...prev, [key]: value })); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(null); setSubmitting(true); try { const res = await fetch("/api/join", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...form, media: media?.url }), }); const data = await res.json(); if (!res.ok || !data?.ok) { throw new Error(data?.error || "提交失败,请稍后重试"); } const userId = data.user_id; if (!userId) { setSubmitted(true); return; } setSubmitting(false); setPayRedirecting(true); const returnUrl = `${window.location.origin}${window.location.pathname}?paid=1`; const payForm = document.createElement("form"); payForm.method = "POST"; payForm.action = "/api/pay"; payForm.target = "_self"; payForm.style.display = "none"; const env = getPayEnv(); const channel = mustUseWxPay(env) ? "wxpay" : payChannel; const device = env === "pc" ? "pc" : "h5"; // PC 跳转 payurl,H5/微信 用 payurl/payurl2 const fields: [string, string][] = [ ["user_id", userId], ["total_fee", "80"], ["type", "meetup"], ["channel", channel], ["device", device], ["return_url", returnUrl], ["html", "1"], ]; fields.forEach(([k, v]) => { const input = document.createElement("input"); input.type = "hidden"; input.name = k; input.value = v; payForm.appendChild(input); }); document.body.appendChild(payForm); payForm.submit(); return; } catch (err) { setError(err instanceof Error ? err.message : "网络错误,请重试"); } finally { setSubmitting(false); } }; if (submitted) { return (

申请已提交

我们会尽快审核你的信息
审核通过后将通过微信邀请你入群

← 返回首页
); } return (
{/* ── PC: center column / H5: full width ── */}
{/* ── Banner ── */}
{/* Hero image area */}
🌍
Digital Nomad Community
{/* Decorative blobs */}
{/* Title block */}

🌍 数字游民社区报名

{"💼 结识志同道合的伙伴\n📍 线下聚会 + 线上分享\n🤝 互助成长,资源共享"}

{/* ── Form card ── */}
{/* ---- Row group: nickname + gender (PC side-by-side) ---- */}
set("nickname", e.target.value)} required className="form-input" />
{/* ---- Row group: single + profession ---- */}
set("profession", e.target.value)} required className="form-input" />
{/* ---- wechat + phone ---- */}
set("wechat", e.target.value)} required className="form-input" /> { const v = e.target.value.replace(/\D/g, "").slice(0, 11); set("phone", v); }} className="form-input" />
{/* ---- Intro (full width) ---- */}