feat(小程序): 入驻申请登录门与预约/测评统一(微信一键登录→一键绑号→短信兜底) + 移除错位的性别Picker

This commit is contained in:
Pine
2026-08-28 17:19:37 +08:00
parent 20e2f0d916
commit 3889456e49
2 changed files with 88 additions and 10 deletions
+83 -10
View File
@@ -1,7 +1,7 @@
import { View, Text, Input, Textarea, Button, Radio, RadioGroup, Checkbox, CheckboxGroup, Picker } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState, useEffect } from 'react';
import { phoneLogin, bindPhone, isAuthed, getUser } from '@/utils/auth';
import { phoneLogin, bindPhone, wxLogin, wxPhoneBind, sendCode, isAuthed, getUser } from '@/utils/auth';
import {
getParks, submitParkApplication,
GENDER_OPTS, COMPANY_TYPE_OPTS, REGION_OPTS, SERVICE_OPTS, DOC_ITEMS, uploadParkDoc,
@@ -19,10 +19,14 @@ const EMPTY = () => ({
});
export default function ParkApply() {
/* 登录态 & 绑号状态(与预约/测评同一套 Gate 逻辑:微信一键 → 短信兜底) */
const [authed, setAuthed] = useState(isAuthed());
const [phoneBound, setPhoneBound] = useState(() => !!(getUser() && getUser().phoneBound));
const [showSms, setShowSms] = useState(false);
const [gatePhone, setGatePhone] = useState('');
const [gateCode, setGateCode] = useState('');
const [gateErr, setGateErr] = useState('');
const [cd, setCd] = useState(0);
const [busy, setBusy] = useState(false);
const [parks, setParks] = useState([]);
@@ -50,6 +54,47 @@ export default function ParkApply() {
return () => { alive = false; };
}, []);
const gateSend = async () => {
if (!/^1\d{10}$/.test(gatePhone)) { setGateErr('请输入正确的 11 位手机号'); return; }
setGateErr('');
setBusy(true);
try {
const r = await sendCode(gatePhone);
setGateErr(`验证码已发送${r.debugCode ? ',演示验证码:' + r.debugCode : ''}5 分钟内有效)`);
setCd(60);
const t = setInterval(() => setCd((v) => { if (v <= 1) { clearInterval(t); return 0; } return v - 1; }), 1000);
} catch (e) { setGateErr((e && e.message) || '发送失败'); }
finally { setBusy(false); }
};
/* 微信一键登录:wx.login + 昵称头像 → 后端建/查 wxid 账号 */
const onWxLogin = async () => {
setGateErr(''); setBusy(true);
try {
let nickName = '', avatarUrl = '';
try { const p = await Taro.getUserProfile({ desc: '用于完善入驻申请资料' }); nickName = p.userInfo.nickName; avatarUrl = p.userInfo.avatarUrl; } catch (e) {}
const { code: loginCode } = await Taro.login();
const d = await wxLogin({ code: loginCode, nickName, avatarUrl });
setAuthed(true);
setPhoneBound(!!d.phoneBound);
} catch (e) { setGateErr((e && e.message) || '微信登录失败'); }
finally { setBusy(false); }
};
/* 微信一键绑定手机号(getPhoneNumber 回调 code */
const onWxPhone = async (e) => {
const pcode = e.detail && e.detail.code;
if (!pcode) { setGateErr('已取消授权绑定'); return; }
setGateErr(''); setBusy(true);
try {
const d = await wxPhoneBind(pcode);
setAuthed(true);
setPhoneBound(!!d.phoneBound);
} catch (err) { setGateErr((err && err.message) || '绑定失败'); }
finally { setBusy(false); }
};
/* 短信兜底:未登录 → phoneLogin(登录即绑号);已登录未绑号 → bindPhone */
const gateSmsLogin = async () => {
setGateErr('');
if (!/^1\d{10}$/.test(gatePhone)) { setGateErr('请输入正确的 11 位手机号'); return; }
@@ -63,10 +108,26 @@ export default function ParkApply() {
await phoneLogin(gatePhone, gateCode);
}
setAuthed(true);
setPhoneBound(true);
} catch (e) { setGateErr((e && e.message) || '登录失败'); }
finally { setBusy(false); }
};
const GateSmsFields = ({ action }) => (
<View className="gate-sms">
<View className="field"><Text className="pa-label">手机号 *</Text>
<Input value={gatePhone} onInput={(e) => setGatePhone(e.detail.value)} type="number" maxlength={11} placeholder="11 位手机号" className="input" />
</View>
<View className="field"><Text className="pa-label">验证码 *</Text>
<View className="pa-row">
<Input value={gateCode} onInput={(e) => setGateCode(e.detail.value)} type="number" maxlength={6} placeholder="6 位验证码" className="input flex" />
<Button className="btn btn-ghost code-btn" disabled={cd > 0} onClick={gateSend}>{cd > 0 ? `${cd}s` : '获取验证码'}</Button>
</View>
</View>
<Button className="btn btn-cta" loading={busy} onClick={gateSmsLogin}>{action}</Button>
</View>
);
// 选择并上传一个资料文件(图片/pdf/doc),存 url
const pickAndUpload = async (key) => {
let filePath = null;
@@ -117,17 +178,32 @@ export default function ParkApply() {
if (loading) return <View className="pa page-in"><Text className="err-text">加载中</Text></View>;
// 未登录/未绑手机 → 快捷门
if (!authed) {
// 未登录/未绑手机 → 快捷门(微信一键 → 短信兜底,与预约/测评一致)
if (!authed || !phoneBound) {
return (
<View className="pa page-in">
<View className="pa-card">
<Text className="pa-title">园区入驻申请</Text>
<Text className="pa-sub">请先用手机号登录验证码 123456</Text>
<View className="field"><Input className="input" placeholder="手机号" value={gatePhone} onInput={(e) => setGatePhone(e.detail.value)} /></View>
<View className="field"><Input className="input" placeholder="验证码" value={gateCode} onInput={(e) => setGateCode(e.detail.value)} /></View>
{!authed ? (
<View className="gate">
<Text className="pa-sub">入驻申请需登录并绑定手机号用于园区审核联系</Text>
<Button className="btn btn-cta" loading={busy} onClick={onWxLogin}>微信一键登录</Button>
{!showSms && (
<Button className="btn btn-ghost gate-toggle" onClick={() => setShowSms(true)}>用短信验证码登录</Button>
)}
{showSms && <GateSmsFields action="登录" />}
</View>
) : (
<View className="gate">
<Text className="pa-sub">绑定手机号后即可提交入驻申请</Text>
<Button className="btn btn-cta" loading={busy} openType="getPhoneNumber" onGetPhoneNumber={onWxPhone}>微信一键绑定手机号</Button>
{!showSms && (
<Button className="btn btn-ghost gate-toggle" onClick={() => setShowSms(true)}>用短信验证码绑定</Button>
)}
{showSms && <GateSmsFields action="绑定" />}
</View>
)}
{gateErr && <Text className="err-text">{gateErr}</Text>}
<Button className="btn btn-cta" loading={busy} onClick={gateSmsLogin}>登录 / 绑定</Button>
</View>
</View>
);
@@ -146,9 +222,6 @@ export default function ParkApply() {
onChange={(e) => setTenantId(parks[Number(e.detail.value)]?.id || '')}>
<View className="pa-picker">{parks.find((p) => p.id === tenantId)?.name || '请选择园区'} </View>
</Picker>
<Picker range={GENDER_OPTS} onChange={(e) => up('gender', GENDER_OPTS[Number(e.detail.value)])}>
<View className="pa-picker">性别{form.gender || '请选择'} </View>
</Picker>
</View>
<View className="pa-card">
@@ -31,6 +31,11 @@
}
.pa-radios, .pa-checks { display: flex; flex-wrap: wrap; gap: 12rpx; align-items: center; }
.pa-radios radio, .pa-checks checkbox { color: #3a4672; font-size: 26rpx; margin: 4rpx 0; }
/* 登录/绑号 Gate(微信一键 + 短信兜底),与 event-detail 保持一致 */
.gate-toggle { margin-top: 16rpx; }
.pa-row { display: flex; gap: 16rpx; align-items: center; }
.pa-row .flex { flex: 1; }
.code-btn { flex-shrink: 0; line-height: 2.4; padding: 0 24rpx; font-size: 24rpx; }
.pa-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20rpx; }
.pa-textarea {
width: 100%; box-sizing: border-box; min-height: 200rpx;