's'
This commit is contained in:
@@ -19,7 +19,7 @@ const ageRangeOptions = ["18-22", "23-27", "28-32", "33-37", "38+"];
|
||||
/** 微信号仅允许英文字母、数字、下划线、连字符 */
|
||||
const WECHAT_REGEX = /^[a-zA-Z0-9_-]*$/;
|
||||
|
||||
/** 从粘贴文本中提取小红书 URL,如 "我在小红书收获了536次赞与收藏>> https://xhslink.com/m/9H50QYiVOVx" */
|
||||
/** 从粘贴文本中提取小红书 URL(主页或笔记链接),清除前后空格 */
|
||||
function extractXiaohongshuUrl(text: string): string {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed) return "";
|
||||
@@ -133,6 +133,7 @@ export default function JoinPage() {
|
||||
const [checkingWechat, setCheckingWechat] = useState(false);
|
||||
|
||||
const [xiaohongshuUrl, setXiaohongshuUrl] = useState("");
|
||||
const [intro, setIntro] = useState("");
|
||||
const [profession, setProfession] = useState("");
|
||||
const [gender, setGender] = useState("");
|
||||
const [ageRange, setAgeRange] = useState("");
|
||||
@@ -146,6 +147,14 @@ export default function JoinPage() {
|
||||
ipLocation?: string;
|
||||
avatarUrl?: string;
|
||||
resolvedUrl?: string;
|
||||
followers?: number;
|
||||
followersText?: string;
|
||||
following?: number;
|
||||
followingText?: string;
|
||||
likesAndCollects?: number;
|
||||
likesAndCollectsText?: string;
|
||||
redbookId?: string;
|
||||
personalLink?: string;
|
||||
} | null>(null);
|
||||
const [validating, setValidating] = useState(false);
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
@@ -210,7 +219,7 @@ export default function JoinPage() {
|
||||
|
||||
const handleUrlBlur = async () => {
|
||||
const extracted = extractXiaohongshuUrl(xiaohongshuUrl);
|
||||
if (extracted !== xiaohongshuUrl) {
|
||||
if (extracted !== xiaohongshuUrl.trim()) {
|
||||
setXiaohongshuUrl(extracted);
|
||||
}
|
||||
const url = extracted;
|
||||
@@ -230,7 +239,7 @@ export default function JoinPage() {
|
||||
});
|
||||
const validateData = await validateRes.json();
|
||||
if (!validateData.valid) {
|
||||
setUrlError(validateData.error || "小红书link异常");
|
||||
setUrlError(validateData.error || "小红书链接异常");
|
||||
return;
|
||||
}
|
||||
const profileRes = await fetch("/api/xiaohongshu/profile", {
|
||||
@@ -248,6 +257,14 @@ export default function JoinPage() {
|
||||
ipLocation: profileData.ipLocation,
|
||||
avatarUrl: profileData.avatarUrl,
|
||||
resolvedUrl: profileData.resolvedUrl,
|
||||
followers: profileData.followers,
|
||||
followersText: profileData.followersText,
|
||||
following: profileData.following,
|
||||
followingText: profileData.followingText,
|
||||
likesAndCollects: profileData.likesAndCollects,
|
||||
likesAndCollectsText: profileData.likesAndCollectsText,
|
||||
redbookId: profileData.redbookId,
|
||||
personalLink: profileData.personalLink,
|
||||
});
|
||||
} else {
|
||||
setUrlError(profileData.error || "获取资料失败");
|
||||
@@ -270,10 +287,22 @@ export default function JoinPage() {
|
||||
setError("该微信号已报名,请勿重复提交");
|
||||
return;
|
||||
}
|
||||
if (!xiaohongshuUrl.trim()) {
|
||||
setError("请填写小红书主页链接或笔记链接");
|
||||
return;
|
||||
}
|
||||
if (xiaohongshuUrl.trim() && urlError) {
|
||||
setError("请先修正小红书链接");
|
||||
return;
|
||||
}
|
||||
if (!intro.trim()) {
|
||||
setError("请填写自我介绍");
|
||||
return;
|
||||
}
|
||||
if (intro.trim().length < 20) {
|
||||
setError("自我介绍不少于20字");
|
||||
return;
|
||||
}
|
||||
if (!profession.trim()) {
|
||||
setError("请填写职业");
|
||||
return;
|
||||
@@ -310,7 +339,7 @@ export default function JoinPage() {
|
||||
const postCountOk = (profile?.postCount ?? 0) > 10;
|
||||
const qualify = formGenderFemale && postCountOk;
|
||||
const genderLabel = gender === "女" ? "女" : gender === "男" ? "男" : "无";
|
||||
const submitXiaohongshuUrl = profile?.resolvedUrl || xiaohongshuUrl.trim() || "";
|
||||
const submitXiaohongshuUrl = profile?.resolvedUrl || extractXiaohongshuUrl(xiaohongshuUrl) || "";
|
||||
|
||||
const res = await fetch("/api/join", {
|
||||
method: "POST",
|
||||
@@ -323,7 +352,27 @@ export default function JoinPage() {
|
||||
xiaohongshu_url: submitXiaohongshuUrl,
|
||||
xiaohongshu_avatar_url: profile?.avatarUrl || "",
|
||||
gender: genderLabel,
|
||||
intro: "",
|
||||
intro: intro.trim(),
|
||||
reason: intro.trim(),
|
||||
userInfo: profile
|
||||
? JSON.stringify({
|
||||
昵称: profile.nickname,
|
||||
性别: profile.gender,
|
||||
笔记数: profile.postCount,
|
||||
笔记数文案: profile.postCountText,
|
||||
IP属地: profile.ipLocation,
|
||||
头像链接: profile.avatarUrl,
|
||||
主页链接: profile.resolvedUrl,
|
||||
粉丝数: profile.followers,
|
||||
粉丝数文案: profile.followersText,
|
||||
关注数: profile.following,
|
||||
关注数文案: profile.followingText,
|
||||
获赞与收藏: profile.likesAndCollects,
|
||||
获赞与收藏文案: profile.likesAndCollectsText,
|
||||
小红书号: profile.redbookId,
|
||||
个人链接: profile.personalLink,
|
||||
})
|
||||
: "",
|
||||
age_range: ageRange,
|
||||
city: "深圳",
|
||||
session: fromVolunteer ? "volunteer" : "digital-nomad",
|
||||
@@ -377,6 +426,7 @@ export default function JoinPage() {
|
||||
setUserRecord(null);
|
||||
setWechat("");
|
||||
setXiaohongshuUrl("");
|
||||
setIntro("");
|
||||
setProfession("");
|
||||
setGender("");
|
||||
setAgeRange("");
|
||||
@@ -813,19 +863,79 @@ export default function JoinPage() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300 mb-2">📱 小红书主页链接</label>
|
||||
<p className="text-xs text-stone-500 dark:text-stone-400 mb-1.5">💡 建议填写,可加快审核</p>
|
||||
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300 mb-2">📱 小红书主页链接 或 笔记链接 <span className="text-red-500">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
value={xiaohongshuUrl}
|
||||
onChange={(e) => { setXiaohongshuUrl(e.target.value); setUrlError(null); }}
|
||||
onBlur={handleUrlBlur}
|
||||
placeholder="https://www.xiaohongshu.com/user/profile/xxx 或粘贴分享文案"
|
||||
required
|
||||
className="form-input"
|
||||
/>
|
||||
{validating && <p className="mt-1.5 text-xs text-amber-600 dark:text-amber-400">🔄 校验中…</p>}
|
||||
{urlError && !validating && <p className="mt-1.5 text-xs text-red-600 dark:text-red-400">⚠️ {urlError}</p>}
|
||||
{profile && !urlError && <p className="mt-1.5 text-xs text-emerald-600 dark:text-emerald-400">已验证</p>}
|
||||
{profile && !urlError && (
|
||||
<>
|
||||
<p className="mt-1.5 text-xs text-emerald-600 dark:text-emerald-400">已验证</p>
|
||||
{process.env.NODE_ENV === "development" && (
|
||||
<div className="mt-3 rounded-xl border border-stone-200 dark:border-stone-600 bg-stone-50 dark:bg-stone-800/50 p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
{profile.avatarUrl ? (
|
||||
<img
|
||||
src={profile.avatarUrl}
|
||||
alt=""
|
||||
className="h-14 w-14 rounded-full object-cover border border-stone-200 dark:border-stone-600"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-amber-100 dark:bg-amber-900/40 text-2xl">
|
||||
👤
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="font-medium text-stone-800 dark:text-stone-100">{profile.nickname}</p>
|
||||
{profile.gender && <p className="text-xs text-stone-500 dark:text-stone-400">性别 {profile.gender}</p>}
|
||||
{profile.redbookId && <p className="text-xs text-stone-500 dark:text-stone-400">小红书号 {profile.redbookId}</p>}
|
||||
{profile.ipLocation && <p className="text-xs text-stone-500 dark:text-stone-400">IP属地 {profile.ipLocation}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-sm text-stone-600 dark:text-stone-400">
|
||||
<span>关注 {profile.followingText ?? profile.following ?? "—"}</span>
|
||||
<span>粉丝 {profile.followersText ?? profile.followers ?? "—"}</span>
|
||||
<span>获赞与收藏 {profile.likesAndCollectsText ?? profile.likesAndCollects ?? "—"}</span>
|
||||
<span>笔记 {profile.postCountText ?? profile.postCount ?? "—"}</span>
|
||||
</div>
|
||||
{profile.personalLink && (
|
||||
<p className="mt-2 text-xs text-stone-500 dark:text-stone-400 truncate">个人链接 {profile.personalLink}</p>
|
||||
)}
|
||||
{profile.resolvedUrl && (
|
||||
<p className="mt-1 text-xs text-stone-400 dark:text-stone-500 truncate">Profile {profile.resolvedUrl}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300 mb-2">✍️ 自我介绍 <span className="text-red-500">*</span></label>
|
||||
<div className="relative">
|
||||
<textarea
|
||||
rows={4}
|
||||
maxLength={500}
|
||||
minLength={20}
|
||||
placeholder="不少于20字"
|
||||
value={intro}
|
||||
onChange={(e) => setIntro(e.target.value)}
|
||||
required
|
||||
className="form-input resize-y min-h-[80px] pr-14"
|
||||
/>
|
||||
<span
|
||||
className={`absolute bottom-2 right-3 text-xs ${
|
||||
intro.length < 20 ? "text-amber-600 dark:text-amber-400" : "text-stone-400 dark:text-stone-500"
|
||||
}`}
|
||||
>
|
||||
{intro.length}/500
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300 mb-2">💼 职业 <span className="text-red-500">*</span></label>
|
||||
|
||||
Reference in New Issue
Block a user