Confirm phone and SMS after bank details, then submit directly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -121,18 +121,11 @@ const PHONE_RE = /^1\d{10}$/;
|
||||
const BANK_RE = /^\d{16,19}$/;
|
||||
|
||||
export function validateStoreStep1(
|
||||
form: Pick<
|
||||
StoreDraftForm,
|
||||
'regionCodes' | 'cityId' | 'name' | 'phone' | 'storeSmsCode' | 'address' | 'intro'
|
||||
>,
|
||||
form: Pick<StoreDraftForm, 'regionCodes' | 'cityId' | 'name' | 'address' | 'intro'>,
|
||||
): string | null {
|
||||
if (!form.regionCodes || form.regionCodes.length < 3) return '请选择省 / 市 / 区县';
|
||||
if (!form.cityId) return '所选地区未匹配到开城城市,请联系总部配置开城区划';
|
||||
if (!form.name.trim()) return '请填写门店名称';
|
||||
if (!form.phone.trim()) return '请填写联系电话';
|
||||
if (!PHONE_RE.test(form.phone.trim())) return '联系电话须为11位手机号';
|
||||
if (!form.storeSmsCode.trim()) return '请输入门店手机号验证码';
|
||||
if (!/^\d{4,6}$/.test(form.storeSmsCode.trim())) return '验证码格式不正确';
|
||||
if (!form.address.trim()) return '请填写详细地址';
|
||||
if (form.intro.trim()) {
|
||||
const len = form.intro.trim().length;
|
||||
@@ -158,11 +151,18 @@ export function patchEnvPhotoAt(urls: string[], index: number, url: string): str
|
||||
}
|
||||
|
||||
export function validateStoreStep3(
|
||||
form: Pick<StoreDraftForm, 'bankAccountName' | 'bankAccountNo' | 'bankBranch'>,
|
||||
form: Pick<
|
||||
StoreDraftForm,
|
||||
'bankAccountName' | 'bankAccountNo' | 'bankBranch' | 'phone' | 'storeSmsCode'
|
||||
>,
|
||||
): string | null {
|
||||
if (!form.bankAccountName.trim()) return '请填写户主姓名';
|
||||
if (!form.bankAccountNo.trim()) return '请填写银行卡号';
|
||||
if (!BANK_RE.test(form.bankAccountNo.replace(/\s/g, ''))) return '银行卡号须为 16~19 位数字';
|
||||
if (!form.bankBranch.trim()) return '请填写开户支行';
|
||||
if (!form.phone.trim()) return '请填写联系电话';
|
||||
if (!PHONE_RE.test(form.phone.trim())) return '联系电话须为11位手机号';
|
||||
if (!form.storeSmsCode.trim()) return '请输入门店手机号验证码';
|
||||
if (!/^\d{4,6}$/.test(form.storeSmsCode.trim())) return '验证码格式不正确';
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -93,8 +93,6 @@ export default function StoreCreatePage() {
|
||||
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const [checkingPhone, setCheckingPhone] = useState(false);
|
||||
|
||||
const [cities, setCities] = useState<OpenCityOption[]>([]);
|
||||
|
||||
const [citiesError, setCitiesError] = useState('');
|
||||
@@ -354,54 +352,10 @@ export default function StoreCreatePage() {
|
||||
const msg = validateStoreStep1(form);
|
||||
|
||||
if (msg) {
|
||||
if (isPhoneValidationMessage(msg)) {
|
||||
if (msg.includes('验证码')) {
|
||||
setFieldErrors({ storeSmsCode: msg });
|
||||
} else {
|
||||
setFieldErrors({ phone: msg });
|
||||
}
|
||||
} else {
|
||||
reportFormError(msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setCheckingPhone(true);
|
||||
|
||||
setSubmitError('');
|
||||
|
||||
setFieldErrors({});
|
||||
|
||||
try {
|
||||
|
||||
const phoneCheck = await checkStorePhoneAvailable(form.phone.trim());
|
||||
|
||||
if (!phoneCheck.available) {
|
||||
const phoneMsg = phoneCheck.message ?? '该手机号已绑定门店,请更换';
|
||||
setFieldErrors({ phone: phoneMsg });
|
||||
return;
|
||||
}
|
||||
if (phoneCheck.needConfirm) {
|
||||
const ok = window.confirm(
|
||||
phoneCheck.message ??
|
||||
`该手机号已是门店主账号(已绑 ${phoneCheck.existingStoreCount ?? 0} 家店),确认后将追加绑定新店。是否继续?`,
|
||||
);
|
||||
if (!ok) {
|
||||
setFieldErrors({ phone: '已取消绑定已有主账号,请更换手机号或确认后继续' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
reportFormError(e instanceof Error ? e.message : '手机号校验失败');
|
||||
return;
|
||||
|
||||
} finally {
|
||||
|
||||
setCheckingPhone(false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
@@ -426,6 +380,14 @@ export default function StoreCreatePage() {
|
||||
const msg = validateStoreStep3(form);
|
||||
|
||||
if (msg) {
|
||||
if (isPhoneValidationMessage(msg)) {
|
||||
if (msg.includes('验证码')) {
|
||||
setFieldErrors({ storeSmsCode: msg });
|
||||
} else {
|
||||
setFieldErrors({ phone: msg });
|
||||
}
|
||||
return;
|
||||
}
|
||||
reportFormError(msg);
|
||||
return;
|
||||
}
|
||||
@@ -433,15 +395,6 @@ export default function StoreCreatePage() {
|
||||
const step1Msg = validateStoreStep1(form);
|
||||
|
||||
if (step1Msg) {
|
||||
if (isPhoneValidationMessage(step1Msg)) {
|
||||
if (step1Msg.includes('验证码')) {
|
||||
setFieldErrors({ storeSmsCode: step1Msg });
|
||||
} else {
|
||||
setFieldErrors({ phone: step1Msg });
|
||||
}
|
||||
goStep(1);
|
||||
return;
|
||||
}
|
||||
reportFormError(step1Msg);
|
||||
goStep(1);
|
||||
return;
|
||||
@@ -481,6 +434,8 @@ export default function StoreCreatePage() {
|
||||
|
||||
});
|
||||
|
||||
setSubmitting(false);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -492,6 +447,7 @@ export default function StoreCreatePage() {
|
||||
);
|
||||
if (!ok) {
|
||||
setFieldErrors({ phone: '已取消绑定已有主账号,请更换手机号或确认后继续' });
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
confirmBindExisting = true;
|
||||
@@ -499,6 +455,7 @@ export default function StoreCreatePage() {
|
||||
|
||||
} catch (e) {
|
||||
reportFormError(e instanceof Error ? e.message : '手机号校验失败');
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -567,7 +524,7 @@ export default function StoreCreatePage() {
|
||||
}
|
||||
if (/验证码/.test(message)) {
|
||||
setFieldErrors({ storeSmsCode: message });
|
||||
goStep(1);
|
||||
goStep(3);
|
||||
return;
|
||||
}
|
||||
setSubmitError(message);
|
||||
@@ -584,7 +541,7 @@ export default function StoreCreatePage() {
|
||||
|
||||
const progress = step === 1 ? 0 : step === 2 ? 50 : 100;
|
||||
|
||||
const nextDisabled = submitting || checkingPhone;
|
||||
const nextDisabled = submitting;
|
||||
|
||||
|
||||
|
||||
@@ -692,104 +649,6 @@ export default function StoreCreatePage() {
|
||||
|
||||
</div>
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>联系电话(门店登录账号) <span className="text-primary">*</span></label>
|
||||
|
||||
<div className="partner-field-input">
|
||||
|
||||
<span className="material-symbols-outlined">call</span>
|
||||
|
||||
<input
|
||||
|
||||
type="tel"
|
||||
|
||||
placeholder="请输入11位手机号"
|
||||
|
||||
value={form.phone}
|
||||
|
||||
onChange={(e) => patchForm({ phone: e.target.value })}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{fieldErrors.phone && (
|
||||
|
||||
<p className="partner-field-error" role="alert">{fieldErrors.phone}</p>
|
||||
|
||||
)}
|
||||
|
||||
<p className="label-md text-muted" style={{ marginTop: 8 }}>
|
||||
|
||||
验证码将发送至该手机号,需门店负责人确认后方可录入
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>门店账号验证码 <span className="text-primary">*</span></label>
|
||||
|
||||
<div className="partner-input-row">
|
||||
|
||||
<div className="partner-input-wrap" style={{ flex: 1 }}>
|
||||
|
||||
<span className="material-symbols-outlined partner-input-icon">shield</span>
|
||||
|
||||
<input
|
||||
|
||||
className="partner-input"
|
||||
|
||||
type="text"
|
||||
|
||||
inputMode="numeric"
|
||||
|
||||
maxLength={6}
|
||||
|
||||
placeholder="请输入短信验证码"
|
||||
|
||||
value={form.storeSmsCode}
|
||||
|
||||
onChange={(e) => patchForm({ storeSmsCode: e.target.value.replace(/\D/g, '') })}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
type="button"
|
||||
|
||||
className="partner-code-btn"
|
||||
|
||||
disabled={smsCooldown > 0 || checkingPhone}
|
||||
|
||||
onClick={() => void sendStorePhoneCode()}
|
||||
|
||||
>
|
||||
|
||||
{smsCooldown > 0 ? `${smsCooldown}s` : '获取验证码'}
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
{smsHint && (
|
||||
|
||||
<p className="label-md text-muted" style={{ marginTop: 8 }}>{smsHint}</p>
|
||||
|
||||
)}
|
||||
|
||||
{fieldErrors.storeSmsCode && (
|
||||
|
||||
<p className="partner-field-error" role="alert">{fieldErrors.storeSmsCode}</p>
|
||||
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>详细地址 <span className="text-primary">*</span></label>
|
||||
@@ -976,46 +835,6 @@ export default function StoreCreatePage() {
|
||||
|
||||
</div>
|
||||
|
||||
<section className="partner-form-card">
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>门店登录手机号 <span className="text-primary">*</span></label>
|
||||
|
||||
<div className="partner-field-input">
|
||||
|
||||
<span className="material-symbols-outlined">call</span>
|
||||
|
||||
<input
|
||||
|
||||
type="tel"
|
||||
|
||||
placeholder="请输入11位手机号"
|
||||
|
||||
value={form.phone}
|
||||
|
||||
onChange={(e) => patchForm({ phone: e.target.value })}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{fieldErrors.phone && (
|
||||
|
||||
<p className="partner-field-error" role="alert">{fieldErrors.phone}</p>
|
||||
|
||||
)}
|
||||
|
||||
<p className="label-md text-muted" style={{ marginTop: 8 }}>
|
||||
|
||||
该手机号将作为门店端登录账号,提交前会再次校验是否已被占用。
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section className="partner-form-card">
|
||||
|
||||
<div className="partner-field">
|
||||
@@ -1056,6 +875,108 @@ export default function StoreCreatePage() {
|
||||
|
||||
</div>
|
||||
|
||||
<section className="partner-form-card">
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>门店登录手机号 <span className="text-primary">*</span></label>
|
||||
|
||||
<div className="partner-field-input">
|
||||
|
||||
<span className="material-symbols-outlined">call</span>
|
||||
|
||||
<input
|
||||
|
||||
type="tel"
|
||||
|
||||
placeholder="请输入11位手机号"
|
||||
|
||||
value={form.phone}
|
||||
|
||||
onChange={(e) => patchForm({ phone: e.target.value })}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{fieldErrors.phone && (
|
||||
|
||||
<p className="partner-field-error" role="alert">{fieldErrors.phone}</p>
|
||||
|
||||
)}
|
||||
|
||||
<p className="label-md text-muted" style={{ marginTop: 8 }}>
|
||||
|
||||
该手机号将作为门店端登录账号,验证码发送至该号确认后方可提交。
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="partner-field">
|
||||
|
||||
<label>门店账号验证码 <span className="text-primary">*</span></label>
|
||||
|
||||
<div className="partner-input-row">
|
||||
|
||||
<div className="partner-input-wrap" style={{ flex: 1 }}>
|
||||
|
||||
<span className="material-symbols-outlined partner-input-icon">shield</span>
|
||||
|
||||
<input
|
||||
|
||||
className="partner-input"
|
||||
|
||||
type="text"
|
||||
|
||||
inputMode="numeric"
|
||||
|
||||
maxLength={6}
|
||||
|
||||
placeholder="请输入短信验证码"
|
||||
|
||||
value={form.storeSmsCode}
|
||||
|
||||
onChange={(e) => patchForm({ storeSmsCode: e.target.value.replace(/\D/g, '') })}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
type="button"
|
||||
|
||||
className="partner-code-btn"
|
||||
|
||||
disabled={smsCooldown > 0 || submitting}
|
||||
|
||||
onClick={() => void sendStorePhoneCode()}
|
||||
|
||||
>
|
||||
|
||||
{smsCooldown > 0 ? `${smsCooldown}s` : '获取验证码'}
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
{smsHint && (
|
||||
|
||||
<p className="label-md text-muted" style={{ marginTop: 8 }}>{smsHint}</p>
|
||||
|
||||
)}
|
||||
|
||||
{fieldErrors.storeSmsCode && (
|
||||
|
||||
<p className="partner-field-error" role="alert">{fieldErrors.storeSmsCode}</p>
|
||||
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</>
|
||||
|
||||
)}
|
||||
@@ -1074,7 +995,7 @@ export default function StoreCreatePage() {
|
||||
|
||||
<button type="button" className="partner-btn-primary" onClick={() => void handleNext()} disabled={nextDisabled}>
|
||||
|
||||
<span>{checkingPhone ? '校验中…' : '下一步'}</span>
|
||||
<span>下一步</span>
|
||||
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>navigate_next</span>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user