This commit is contained in:
eric
2026-03-08 03:40:53 -05:00
parent f589b71b19
commit 51eeea3be3
8 changed files with 747 additions and 138 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { useState, useRef, type FormEvent, type ChangeEvent } from "react";
import { useState, useRef, useEffect, type FormEvent, type ChangeEvent } from "react";
import { getPayEnv, getRecommendedChannel, mustUseWxPay, type PayChannel, type PayEnv } from "../lib/payEnv";
const genderOptions = ["男", "女"];
const singleOptions = ["单身", "恋爱中", "已婚"];
@@ -43,6 +44,22 @@ export default function JoinPage() {
const [submitted, setSubmitted] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [payRedirecting, setPayRedirecting] = useState(false);
const [payEnv, setPayEnv] = useState<PayEnv | null>(null);
const [payChannel, setPayChannel] = useState<PayChannel>("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 }));
@@ -61,7 +78,41 @@ export default function JoinPage() {
if (!res.ok || !data?.ok) {
throw new Error(data?.error || "提交失败,请稍后重试");
}
setSubmitted(true);
const userId = data.user_id;
if (!userId) {
setSubmitted(true);
return;
}
setSubmitting(false);
setPayRedirecting(true);
const returnUrl = `${window.location.origin}/join?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 跳转 payurlH5/微信 用 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 {
@@ -395,6 +446,41 @@ export default function JoinPage() {
)}
</div>
{/* ── 支付通道选择PC/H5 可选,微信内强制微信支付)── */}
<div className="mt-6 rounded-xl border border-slate-100 bg-slate-50/50 px-4 py-4 sm:px-5">
<p className="mb-3 text-sm font-bold text-slate-700">💳 </p>
{payEnv === "wechat" ? (
<p className="text-sm text-slate-500">
使 <span className="font-medium text-emerald-600"></span>
</p>
) : (
<div className="flex gap-3">
<label className="flex flex-1 cursor-pointer items-center gap-2 rounded-lg border-2 px-4 py-3 transition-colors has-[:checked]:border-sky-500 has-[:checked]:bg-sky-50">
<input
type="radio"
name="payChannel"
value="alipay"
checked={payChannel === "alipay"}
onChange={() => setPayChannel("alipay")}
className="h-4 w-4 border-slate-300 text-sky-500"
/>
<span className="text-sm font-medium text-slate-700"></span>
</label>
<label className="flex flex-1 cursor-pointer items-center gap-2 rounded-lg border-2 px-4 py-3 transition-colors has-[:checked]:border-emerald-500 has-[:checked]:bg-emerald-50">
<input
type="radio"
name="payChannel"
value="wxpay"
checked={payChannel === "wxpay"}
onChange={() => setPayChannel("wxpay")}
className="h-4 w-4 border-slate-300 text-emerald-500"
/>
<span className="text-sm font-medium text-slate-700"></span>
</label>
</div>
)}
</div>
{/* ── Submit ── */}
<div className="mt-8 sm:mt-10">
{error && (
@@ -404,15 +490,12 @@ export default function JoinPage() {
)}
<button
type="submit"
disabled={submitting || uploading}
disabled={submitting || uploading || payRedirecting}
className="w-full rounded-full bg-[#1aad19] py-3.5 text-lg font-semibold text-white shadow-md shadow-emerald-500/20 transition-all hover:bg-[#179b16] hover:shadow-lg hover:shadow-emerald-500/25 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-70 sm:py-4"
>
{uploading ? "媒体上传中…" : submitting ? "提交中…" : "提交申请"}
{uploading ? "媒体上传中…" : payRedirecting ? "跳转支付中…" : submitting ? "提交中…" : "提交申请"}
</button>
<p className="mt-4 whitespace-pre-line text-center text-sm leading-relaxed text-amber-500">
{"🔍 提交后将进行审核\n💰 需支付基本场地/管理费"}
</p>
</div>
</form>