Files
gitlab-instance-0a899031_no…/src/pages/salon/index.tsx
2026-02-03 04:41:43 -06:00

500 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;