feat(小程序): 入驻申请登录门与预约/测评统一(微信一键登录→一键绑号→短信兜底) + 移除错位的性别Picker
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user