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}$/;
|
const BANK_RE = /^\d{16,19}$/;
|
||||||
|
|
||||||
export function validateStoreStep1(
|
export function validateStoreStep1(
|
||||||
form: Pick<
|
form: Pick<StoreDraftForm, 'regionCodes' | 'cityId' | 'name' | 'address' | 'intro'>,
|
||||||
StoreDraftForm,
|
|
||||||
'regionCodes' | 'cityId' | 'name' | 'phone' | 'storeSmsCode' | 'address' | 'intro'
|
|
||||||
>,
|
|
||||||
): string | null {
|
): string | null {
|
||||||
if (!form.regionCodes || form.regionCodes.length < 3) return '请选择省 / 市 / 区县';
|
if (!form.regionCodes || form.regionCodes.length < 3) return '请选择省 / 市 / 区县';
|
||||||
if (!form.cityId) return '所选地区未匹配到开城城市,请联系总部配置开城区划';
|
if (!form.cityId) return '所选地区未匹配到开城城市,请联系总部配置开城区划';
|
||||||
if (!form.name.trim()) 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.address.trim()) return '请填写详细地址';
|
||||||
if (form.intro.trim()) {
|
if (form.intro.trim()) {
|
||||||
const len = form.intro.trim().length;
|
const len = form.intro.trim().length;
|
||||||
@@ -158,11 +151,18 @@ export function patchEnvPhotoAt(urls: string[], index: number, url: string): str
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function validateStoreStep3(
|
export function validateStoreStep3(
|
||||||
form: Pick<StoreDraftForm, 'bankAccountName' | 'bankAccountNo' | 'bankBranch'>,
|
form: Pick<
|
||||||
|
StoreDraftForm,
|
||||||
|
'bankAccountName' | 'bankAccountNo' | 'bankBranch' | 'phone' | 'storeSmsCode'
|
||||||
|
>,
|
||||||
): string | null {
|
): string | null {
|
||||||
if (!form.bankAccountName.trim()) return '请填写户主姓名';
|
if (!form.bankAccountName.trim()) return '请填写户主姓名';
|
||||||
if (!form.bankAccountNo.trim()) return '请填写银行卡号';
|
if (!form.bankAccountNo.trim()) return '请填写银行卡号';
|
||||||
if (!BANK_RE.test(form.bankAccountNo.replace(/\s/g, ''))) return '银行卡号须为 16~19 位数字';
|
if (!BANK_RE.test(form.bankAccountNo.replace(/\s/g, ''))) return '银行卡号须为 16~19 位数字';
|
||||||
if (!form.bankBranch.trim()) return '请填写开户支行';
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
const [checkingPhone, setCheckingPhone] = useState(false);
|
|
||||||
|
|
||||||
const [cities, setCities] = useState<OpenCityOption[]>([]);
|
const [cities, setCities] = useState<OpenCityOption[]>([]);
|
||||||
|
|
||||||
const [citiesError, setCitiesError] = useState('');
|
const [citiesError, setCitiesError] = useState('');
|
||||||
@@ -354,54 +352,10 @@ export default function StoreCreatePage() {
|
|||||||
const msg = validateStoreStep1(form);
|
const msg = validateStoreStep1(form);
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
if (isPhoneValidationMessage(msg)) {
|
reportFormError(msg);
|
||||||
if (msg.includes('验证码')) {
|
|
||||||
setFieldErrors({ storeSmsCode: msg });
|
|
||||||
} else {
|
|
||||||
setFieldErrors({ phone: msg });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reportFormError(msg);
|
|
||||||
}
|
|
||||||
return;
|
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) {
|
if (step === 2) {
|
||||||
@@ -426,6 +380,14 @@ export default function StoreCreatePage() {
|
|||||||
const msg = validateStoreStep3(form);
|
const msg = validateStoreStep3(form);
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
|
if (isPhoneValidationMessage(msg)) {
|
||||||
|
if (msg.includes('验证码')) {
|
||||||
|
setFieldErrors({ storeSmsCode: msg });
|
||||||
|
} else {
|
||||||
|
setFieldErrors({ phone: msg });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
reportFormError(msg);
|
reportFormError(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -433,15 +395,6 @@ export default function StoreCreatePage() {
|
|||||||
const step1Msg = validateStoreStep1(form);
|
const step1Msg = validateStoreStep1(form);
|
||||||
|
|
||||||
if (step1Msg) {
|
if (step1Msg) {
|
||||||
if (isPhoneValidationMessage(step1Msg)) {
|
|
||||||
if (step1Msg.includes('验证码')) {
|
|
||||||
setFieldErrors({ storeSmsCode: step1Msg });
|
|
||||||
} else {
|
|
||||||
setFieldErrors({ phone: step1Msg });
|
|
||||||
}
|
|
||||||
goStep(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
reportFormError(step1Msg);
|
reportFormError(step1Msg);
|
||||||
goStep(1);
|
goStep(1);
|
||||||
return;
|
return;
|
||||||
@@ -481,6 +434,8 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setSubmitting(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -492,6 +447,7 @@ export default function StoreCreatePage() {
|
|||||||
);
|
);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
setFieldErrors({ phone: '已取消绑定已有主账号,请更换手机号或确认后继续' });
|
setFieldErrors({ phone: '已取消绑定已有主账号,请更换手机号或确认后继续' });
|
||||||
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
confirmBindExisting = true;
|
confirmBindExisting = true;
|
||||||
@@ -499,6 +455,7 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reportFormError(e instanceof Error ? e.message : '手机号校验失败');
|
reportFormError(e instanceof Error ? e.message : '手机号校验失败');
|
||||||
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +524,7 @@ export default function StoreCreatePage() {
|
|||||||
}
|
}
|
||||||
if (/验证码/.test(message)) {
|
if (/验证码/.test(message)) {
|
||||||
setFieldErrors({ storeSmsCode: message });
|
setFieldErrors({ storeSmsCode: message });
|
||||||
goStep(1);
|
goStep(3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSubmitError(message);
|
setSubmitError(message);
|
||||||
@@ -584,7 +541,7 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
const progress = step === 1 ? 0 : step === 2 ? 50 : 100;
|
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>
|
||||||
|
|
||||||
<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">
|
<div className="partner-field">
|
||||||
|
|
||||||
<label>详细地址 <span className="text-primary">*</span></label>
|
<label>详细地址 <span className="text-primary">*</span></label>
|
||||||
@@ -976,46 +835,6 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
</div>
|
</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">
|
<section className="partner-form-card">
|
||||||
|
|
||||||
<div className="partner-field">
|
<div className="partner-field">
|
||||||
@@ -1056,6 +875,108 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
</div>
|
</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}>
|
<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>
|
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>navigate_next</span>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user