500 lines
13 KiB
TypeScript
500 lines
13 KiB
TypeScript
import { View, Text, Image, Picker } from "@tarojs/components";
|
||
import "./index.scss";
|
||
import {
|
||
AtButton,
|
||
AtInput,
|
||
AtForm,
|
||
AtMessage,
|
||
AtList,
|
||
AtListItem,
|
||
AtTextarea,
|
||
} from "taro-ui";
|
||
import React, { useEffect, useState } from "react";
|
||
import Taro from "@tarojs/taro";
|
||
|
||
import loft from "../../images/loft.png";
|
||
import qrcode from "../../images/qrcode.png";
|
||
import joinGroup from "../../images/joinGroup.png";
|
||
|
||
const PB_URL = "https://pocketbase.hackrobot.cn";
|
||
const COLLECTION = "solan";
|
||
const PAY_API_URL = "https://api.hackrobot.cn/payh5";
|
||
const SUBMIT_APPLICATION_URL =
|
||
"https://api.hackrobot.cn/submit_meetup_application";
|
||
|
||
const Salon = () => {
|
||
const [meetupQR, setmeetupQR] = useState(false);
|
||
const [isWeChat, setisWeChat] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const init = async () => {
|
||
isWeChatFun();
|
||
checkIfJoined();
|
||
|
||
const userId = getOrCreateUserId();
|
||
const fullyPaid = await checkIfFullyPaid(userId);
|
||
|
||
if (fullyPaid) {
|
||
Taro.setStorageSync("salonJoined", true);
|
||
setmeetupQR(true);
|
||
}
|
||
};
|
||
init();
|
||
}, []);
|
||
|
||
const checkIfJoined = () => {
|
||
if (Taro.getStorageSync("salonJoined")) {
|
||
setmeetupQR(true);
|
||
}
|
||
};
|
||
|
||
const isWeChatFun = () => {
|
||
const ua = navigator.userAgent.toLowerCase();
|
||
if (ua.indexOf("micromessenger") !== -1) {
|
||
setisWeChat(true);
|
||
return true;
|
||
} else {
|
||
setisWeChat(false);
|
||
return false;
|
||
}
|
||
};
|
||
|
||
const getOrCreateUserId = () => {
|
||
let userId = Taro.getStorageSync("userId");
|
||
if (!userId) {
|
||
userId = `user${Date.now()}`;
|
||
Taro.setStorageSync("userId", userId);
|
||
}
|
||
return userId;
|
||
};
|
||
|
||
const checkIfFullyPaid = (userId) => {
|
||
return new Promise((resolve) => {
|
||
Taro.request({
|
||
url: `${PB_URL}/api/collections/${COLLECTION}/records`,
|
||
method: "GET",
|
||
data: {
|
||
filter: `(user_id="${userId}" && amount > 0 && type="meetup")`,
|
||
perPage: 1,
|
||
},
|
||
success: (res) => {
|
||
resolve(res.data?.items?.length > 0);
|
||
},
|
||
fail: () => resolve(false),
|
||
});
|
||
});
|
||
};
|
||
|
||
const payh5 = (type, total_fee = 8800) => {
|
||
const userId = getOrCreateUserId();
|
||
Taro.request({
|
||
url: PAY_API_URL,
|
||
method: "POST",
|
||
data: {
|
||
total_fee,
|
||
type,
|
||
user_id: userId,
|
||
},
|
||
header: {
|
||
"content-type": "application/json",
|
||
},
|
||
success: (res) => {
|
||
if (res.data?.status === "ok" && res.data?.xorpay_params) {
|
||
const params = res.data.xorpay_params;
|
||
const form = document.createElement("form");
|
||
form.method = "POST";
|
||
form.action = res.data.xorpay_url;
|
||
form.style.display = "none";
|
||
|
||
for (const key in params) {
|
||
const input = document.createElement("input");
|
||
input.type = "hidden";
|
||
input.name = key;
|
||
input.value = params[key];
|
||
form.appendChild(input);
|
||
}
|
||
|
||
document.body.appendChild(form);
|
||
form.submit();
|
||
} else {
|
||
Taro.showToast({ title: "支付发起失败", icon: "none" });
|
||
}
|
||
},
|
||
fail: () => {
|
||
Taro.showToast({ title: "网络错误", icon: "none" });
|
||
},
|
||
});
|
||
};
|
||
|
||
const [form, setForm] = useState({
|
||
nickname: "",
|
||
occupation: "",
|
||
reason: "",
|
||
wechatId: "",
|
||
gender: "",
|
||
education: "",
|
||
gradYear: "",
|
||
phone: "",
|
||
});
|
||
|
||
const genderOptions = ["男", "女"];
|
||
const [genderIndex, setGenderIndex] = useState(null);
|
||
|
||
const educationOptions = ["高中及以下", "大专", "本科", "硕士"];
|
||
const [educationIndex, setEducationIndex] = useState(null);
|
||
|
||
const currentYear = new Date().getFullYear();
|
||
const yearOptions = [];
|
||
for (let i = 0; i <= 60; i++) {
|
||
yearOptions.push((currentYear - i).toString());
|
||
}
|
||
const [gradYearIndex, setGradYearIndex] = useState(null);
|
||
|
||
const updateField = (field) => (value) => {
|
||
setForm((prev) => ({ ...prev, [field]: value }));
|
||
};
|
||
|
||
const handleGenderChange = (e) => {
|
||
const idx = e.detail.value;
|
||
setGenderIndex(idx);
|
||
updateField("gender")(genderOptions[idx]);
|
||
};
|
||
|
||
const handleEducationChange = (e) => {
|
||
const idx = e.detail.value;
|
||
setEducationIndex(idx);
|
||
updateField("education")(educationOptions[idx]);
|
||
};
|
||
|
||
const handleGradYearChange = (e) => {
|
||
const idx = e.detail.value;
|
||
setGradYearIndex(idx);
|
||
updateField("gradYear")(yearOptions[idx]);
|
||
};
|
||
|
||
const submitApplicationFirst = (submitData) => {
|
||
return new Promise((resolve, reject) => {
|
||
Taro.request({
|
||
url: SUBMIT_APPLICATION_URL,
|
||
method: "POST",
|
||
header: { "Content-Type": "application/json" },
|
||
data: submitData,
|
||
success: (res) => {
|
||
if (res.statusCode === 200 && res.data?.status === "ok") {
|
||
resolve(true);
|
||
} else {
|
||
Taro.showToast({ title: "申请提交失败", icon: "none" });
|
||
reject();
|
||
}
|
||
},
|
||
fail: () => {
|
||
Taro.showToast({ title: "网络错误,请重试", icon: "none" });
|
||
reject();
|
||
},
|
||
});
|
||
});
|
||
};
|
||
|
||
const handleSubmit = async () => {
|
||
let missing = [];
|
||
|
||
if (!form.nickname) missing.push("昵称");
|
||
if (!form.occupation) missing.push("职业/专业");
|
||
if (!form.reason) missing.push("自我介绍");
|
||
if (!form.wechatId) missing.push("微信号");
|
||
if (!form.gender) missing.push("性别");
|
||
if (!form.education) missing.push("学历");
|
||
if (!form.gradYear) missing.push("毕业时间");
|
||
if (!form.phone) missing.push("手机号"); // 手机号必填
|
||
|
||
if (missing.length > 0) {
|
||
Taro.atMessage({
|
||
message: `请填写:${missing.join("、")}`,
|
||
type: "warning",
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 手机号正则校验(必填,必须11位有效手机号)
|
||
if (!/^1[3-9]\d{9}$/.test(form.phone)) {
|
||
Taro.atMessage({
|
||
message: "手机号格式错误,请填写11位有效手机号",
|
||
type: "warning",
|
||
});
|
||
return;
|
||
}
|
||
|
||
const gradAgeMap = {
|
||
高中及以下: 18,
|
||
大专: 21,
|
||
本科: 22,
|
||
硕士: 25,
|
||
};
|
||
const baseAge = gradAgeMap[form.education] || 22;
|
||
const gradYearNum = parseInt(form.gradYear, 10);
|
||
|
||
if (isNaN(gradYearNum)) {
|
||
Taro.atMessage({
|
||
message: "毕业年份格式错误",
|
||
type: "warning",
|
||
});
|
||
return;
|
||
}
|
||
|
||
const calculatedAge = currentYear - gradYearNum + baseAge;
|
||
|
||
const userId = getOrCreateUserId();
|
||
|
||
const submitData = {
|
||
...form,
|
||
calculated_age: calculatedAge,
|
||
user_id: userId,
|
||
};
|
||
|
||
try {
|
||
await submitApplicationFirst(submitData);
|
||
|
||
Taro.showToast({
|
||
title: "申请已提交,正在跳转支付(88元场地管理费)",
|
||
icon: "loading",
|
||
duration: 3000,
|
||
});
|
||
|
||
payh5("meetup", 28800);
|
||
} catch {
|
||
// 提交失败已提示
|
||
}
|
||
};
|
||
|
||
if (meetupQR) {
|
||
return (
|
||
<View
|
||
className="Model"
|
||
style={{ padding: "20px", textAlign: "center", background: "#f8f9fa" }}
|
||
>
|
||
<Image
|
||
style="width: 100%; height: 120px; borderRadius: 12px; boxShadow: 0 4px 12px rgba(0,0,0,0.1);"
|
||
src={loft}
|
||
/>
|
||
<View style={{ marginTop: "40px" }}>
|
||
<Text
|
||
style={{ fontSize: "24px", fontWeight: "bold", color: "#2c3e50" }}
|
||
>
|
||
🎉 欢迎加入数字游民社区!
|
||
</Text>
|
||
</View>
|
||
<View style={{ margin: "30px 0" }}>
|
||
{isWeChat ? (
|
||
<Image
|
||
src={qrcode}
|
||
mode="widthFix"
|
||
style="width: 80%; borderRadius: 12px; boxShadow: 0 4px 12px rgba(0,0,0,0.1);"
|
||
/>
|
||
) : (
|
||
<View>
|
||
<Text style={{ fontSize: "16px", color: "#e74c3c" }}>
|
||
🔴 请在微信中打开本页面以查看社群二维码
|
||
</Text>
|
||
<Image
|
||
src={joinGroup}
|
||
mode="widthFix"
|
||
style="width: 80%; marginTop: 20px; borderRadius: 12px;"
|
||
/>
|
||
</View>
|
||
)}
|
||
</View>
|
||
<Text style={{ fontSize: "14px", color: "#7f8c8d", marginTop: "20px" }}>
|
||
💡 入群后请遵守社区规则,共同维护良好氛围~
|
||
</Text>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<View
|
||
className="Model"
|
||
style={{ background: "#f0f4f8", minHeight: "100vh" }}
|
||
>
|
||
<View
|
||
className="meetup-left"
|
||
style={{ background: "#fff", boxShadow: "0 4px 12px rgba(0,0,0,0.05)" }}
|
||
>
|
||
<Image
|
||
style="width: 100%; height: 140px; borderRadius: '12px 12px 0 0';"
|
||
src={loft}
|
||
/>
|
||
<View style={{ padding: "20px", textAlign: "center" }}>
|
||
<Text
|
||
style={{ fontSize: "22px", fontWeight: "bold", color: "#2c3e50" }}
|
||
>
|
||
🌍 数字游民社区报名
|
||
</Text>
|
||
<br />
|
||
<Text
|
||
style={{
|
||
fontSize: "14px",
|
||
color: "#7f8c8d",
|
||
marginTop: "10px",
|
||
lineHeight: "1.6",
|
||
whiteSpace: "pre-line",
|
||
}}
|
||
>
|
||
💼 结识志同道合的伙伴
|
||
<br />
|
||
📍 线下聚会 + 线上分享
|
||
<br />
|
||
🤝 互助成长,资源共享
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View
|
||
className="index-page"
|
||
style={{
|
||
padding: "20px",
|
||
background: "#fff",
|
||
marginTop: "20px",
|
||
borderRadius: "16px",
|
||
boxShadow: "0 4px 20px rgba(0,0,0,0.08)",
|
||
}}
|
||
>
|
||
<AtMessage />
|
||
|
||
<AtForm onSubmit={handleSubmit}>
|
||
<AtInput
|
||
title="🌟 昵称"
|
||
type="text"
|
||
placeholder="请输入您的昵称"
|
||
value={form.nickname}
|
||
onChange={updateField("nickname")}
|
||
/>
|
||
|
||
<Picker
|
||
mode="selector"
|
||
range={genderOptions}
|
||
value={genderIndex ?? 0}
|
||
onChange={handleGenderChange}
|
||
>
|
||
<View className="picker-item">
|
||
<AtListItem
|
||
title="👤 性别"
|
||
extraText={form.gender || "请选择"}
|
||
arrow="right"
|
||
/>
|
||
</View>
|
||
</Picker>
|
||
|
||
<AtInput
|
||
title="💼 职业"
|
||
type="text"
|
||
placeholder="您的职业或专业"
|
||
value={form.occupation}
|
||
onChange={updateField("occupation")}
|
||
/>
|
||
|
||
<View style={{ margin: "30px 0" }}>
|
||
<Text
|
||
style={{
|
||
fontSize: "16px",
|
||
fontWeight: "bold",
|
||
color: "#2c3e50",
|
||
marginBottom: "10px",
|
||
display: "block",
|
||
}}
|
||
>
|
||
📝 自我介绍
|
||
</Text>
|
||
<AtTextarea
|
||
value={form.reason}
|
||
onChange={updateField("reason")}
|
||
maxLength={500}
|
||
height={180}
|
||
placeholder="分享您的经历、兴趣或加入原因,越详细越好哦~"
|
||
style={{ borderRadius: "12px", background: "#f8f9fa" }}
|
||
/>
|
||
</View>
|
||
|
||
<AtInput
|
||
title="💬 微信号"
|
||
type="text"
|
||
placeholder="用于拉群"
|
||
value={form.wechatId}
|
||
onChange={updateField("wechatId")}
|
||
/>
|
||
|
||
<Picker
|
||
mode="selector"
|
||
range={educationOptions}
|
||
value={educationIndex ?? 0}
|
||
onChange={handleEducationChange}
|
||
>
|
||
<View className="picker-item">
|
||
<AtListItem
|
||
title="🎓 学历"
|
||
extraText={form.education || "请选择"}
|
||
arrow="right"
|
||
/>
|
||
</View>
|
||
</Picker>
|
||
|
||
<Picker
|
||
mode="selector"
|
||
range={yearOptions}
|
||
value={gradYearIndex ?? 0}
|
||
onChange={handleGradYearChange}
|
||
>
|
||
<View className="picker-item">
|
||
<AtListItem
|
||
title="📅 毕业时间"
|
||
extraText={form.gradYear || "请选择"}
|
||
arrow="right"
|
||
/>
|
||
</View>
|
||
</Picker>
|
||
|
||
<AtInput
|
||
title="📱 手机号"
|
||
type="phone"
|
||
placeholder="用于紧急联系"
|
||
value={form.phone}
|
||
onChange={updateField("phone")}
|
||
/>
|
||
|
||
<View style={{ marginTop: "40px" }}>
|
||
<AtButton
|
||
formType="submit"
|
||
type="primary"
|
||
size="normal"
|
||
circle
|
||
style={{
|
||
background: "#27ae60",
|
||
borderColor: "#27ae60",
|
||
fontSize: "18px",
|
||
fontWeight: "bold",
|
||
}}
|
||
>
|
||
提交申请
|
||
</AtButton>
|
||
<View style={{ marginTop: "16px", textAlign: "center" }}>
|
||
<Text
|
||
style={{
|
||
fontSize: "14px",
|
||
color: "#e67e22",
|
||
lineHeight: "1.6",
|
||
whiteSpace: "pre-line",
|
||
}}
|
||
>
|
||
🔍 提交后将进行审核
|
||
<br />
|
||
💰 需支付基本场地/管理费
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</AtForm>
|
||
</View>
|
||
</View>
|
||
);
|
||
};
|
||
|
||
export default Salon;
|