合伙人端验证是否已经绑定手机号,否则不支持微信一键登录
This commit is contained in:
@@ -19,7 +19,7 @@ export default function AuthGate({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
if (!authenticated && !PUBLIC_PATHS.has(location.pathname)) {
|
if (!authenticated && !PUBLIC_PATHS.has(location.pathname)) {
|
||||||
const profile = getPartnerProfile();
|
const profile = getPartnerProfile();
|
||||||
if (profile && hasPartnerWxSession()) {
|
if (profile && hasPartnerWxSession() && profile.hasWechat) {
|
||||||
return <Navigate to="/login?quick=1" replace state={{ from: location }} />;
|
return <Navigate to="/login?quick=1" replace state={{ from: location }} />;
|
||||||
}
|
}
|
||||||
return <Navigate to="/login" replace state={{ from: location }} />;
|
return <Navigate to="/login" replace state={{ from: location }} />;
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ export function clearStoreDraft(accountId?: string) {
|
|||||||
localStorage.removeItem(storeDraftKey(accountId));
|
localStorage.removeItem(storeDraftKey(accountId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 清除当前账号及遗留的全局录店草稿 */
|
||||||
|
export function clearAllStoreDrafts(accountId?: string) {
|
||||||
|
clearStoreDraft(accountId);
|
||||||
|
localStorage.removeItem(STORE_DRAFT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
const PHONE_RE = /^1\d{10}$/;
|
const PHONE_RE = /^1\d{10}$/;
|
||||||
const BANK_RE = /^\d{16,19}$/;
|
const BANK_RE = /^\d{16,19}$/;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import type { ClientRuntimeConfig, WechatLoginResult } from '@dukang/shared-types';
|
import type { ClientRuntimeConfig, WechatLoginResult } from '@dukang/shared-types';
|
||||||
|
import type { PartnerPhoneCheckResponse } from '@dukang/shared-types';
|
||||||
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
|
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
|
||||||
import { WECHAT_INAPP_REQUIRED_MSG } from '@dukang/weixin-sdk';
|
import { WECHAT_INAPP_REQUIRED_MSG } from '@dukang/weixin-sdk';
|
||||||
import { isWechatEnv, weixinSdk } from './weixin';
|
import { isWechatEnv, weixinSdk } from './weixin';
|
||||||
import { request, saveWechatSession, type PartnerSessionPayload } from './api';
|
import { getPartnerProfile, request, saveWechatSession, type PartnerSessionPayload, type PartnerSessionProfile } from './api';
|
||||||
|
|
||||||
|
export const PARTNER_WECHAT_LOGIN_HINT =
|
||||||
|
'请先使用手机验证码登录,登录后将自动关联微信';
|
||||||
|
|
||||||
export type PartnerProfile = {
|
export type PartnerProfile = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -13,6 +17,35 @@ export type PartnerProfile = {
|
|||||||
isPrimary?: boolean;
|
isPrimary?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function partnerHasWechatBinding(
|
||||||
|
profile: PartnerProfile | PartnerSessionProfile | null | undefined,
|
||||||
|
): boolean {
|
||||||
|
return !!profile?.hasWechat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 微信一键登录前:账号须已绑定 wxOpenId(本地缓存或手机号校验) */
|
||||||
|
export async function canPartnerUseWechatLogin(options: {
|
||||||
|
profile?: PartnerProfile | PartnerSessionProfile | null;
|
||||||
|
phone?: string;
|
||||||
|
}): Promise<boolean> {
|
||||||
|
const cached = options.profile ?? getPartnerProfile();
|
||||||
|
if (partnerHasWechatBinding(cached)) return true;
|
||||||
|
|
||||||
|
const normalized = options.phone?.trim() ?? '';
|
||||||
|
if (!/^1\d{10}$/.test(normalized)) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await request<PartnerPhoneCheckResponse>('PARTNER_H5', '/partner/auth/phone/check', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ phone: normalized }),
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
return !!res.hasWechat;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchClientConfig(): Promise<ClientRuntimeConfig> {
|
export async function fetchClientConfig(): Promise<ClientRuntimeConfig> {
|
||||||
return request<ClientRuntimeConfig>('PARTNER_H5', '/common/client-config');
|
return request<ClientRuntimeConfig>('PARTNER_H5', '/common/client-config');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import { usePartnerSession } from '../contexts/PartnerSessionContext';
|
|||||||
import { partnerHomePath } from '../lib/partnerAccess';
|
import { partnerHomePath } from '../lib/partnerAccess';
|
||||||
import {
|
import {
|
||||||
bindPartnerWechatAfterSmsLogin,
|
bindPartnerWechatAfterSmsLogin,
|
||||||
|
canPartnerUseWechatLogin,
|
||||||
fetchClientConfig,
|
fetchClientConfig,
|
||||||
loginPartnerWithWechat,
|
loginPartnerWithWechat,
|
||||||
|
PARTNER_WECHAT_LOGIN_HINT,
|
||||||
} from '../lib/wechat-auth';
|
} from '../lib/wechat-auth';
|
||||||
import { isWechatEnv } from '../lib/weixin';
|
import { isWechatEnv } from '../lib/weixin';
|
||||||
|
|
||||||
@@ -168,6 +170,14 @@ export default function LoginPage() {
|
|||||||
setMsg(WECHAT_INAPP_REQUIRED_MSG);
|
setMsg(WECHAT_INAPP_REQUIRED_MSG);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const canUseWechat = await canPartnerUseWechatLogin({
|
||||||
|
profile: savedProfile,
|
||||||
|
phone,
|
||||||
|
});
|
||||||
|
if (!canUseWechat) {
|
||||||
|
setMsg(PARTNER_WECHAT_LOGIN_HINT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setWxLoading(true);
|
setWxLoading(true);
|
||||||
try {
|
try {
|
||||||
const session = await loginPartnerWithWechat();
|
const session = await loginPartnerWithWechat();
|
||||||
@@ -183,7 +193,12 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (quick) {
|
if (quick) {
|
||||||
const canWechatQuick = wxAuthorize && isWechatEnv() && hasPartnerWxSession() && !!savedProfile;
|
const canWechatQuick =
|
||||||
|
wxAuthorize &&
|
||||||
|
isWechatEnv() &&
|
||||||
|
hasPartnerWxSession() &&
|
||||||
|
!!savedProfile &&
|
||||||
|
savedProfile.hasWechat === true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="partner-auth-page partner-auth-page--quick">
|
<div className="partner-auth-page partner-auth-page--quick">
|
||||||
@@ -334,7 +349,7 @@ export default function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{hasPartnerWxSession() && savedProfile && (
|
{hasPartnerWxSession() && savedProfile?.hasWechat && (
|
||||||
<Link to="/login?quick=1" className="partner-link">微信快捷登录</Link>
|
<Link to="/login?quick=1" className="partner-link">微信快捷登录</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ import { isSubAccount } from '../lib/partnerAccess';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
||||||
clearStoreDraft,
|
clearAllStoreDrafts,
|
||||||
|
|
||||||
defaultStoreForm,
|
defaultStoreForm,
|
||||||
|
|
||||||
@@ -114,6 +114,8 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
const [smsHint, setSmsHint] = useState('');
|
const [smsHint, setSmsHint] = useState('');
|
||||||
|
|
||||||
|
const draftSaveDisabledRef = useRef(false);
|
||||||
|
|
||||||
function reportFormError(message: string) {
|
function reportFormError(message: string) {
|
||||||
setSubmitError(message);
|
setSubmitError(message);
|
||||||
}
|
}
|
||||||
@@ -137,9 +139,8 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (draftSaveDisabledRef.current) return;
|
||||||
saveStoreDraft({ step, form }, accountId);
|
saveStoreDraft({ step, form }, accountId);
|
||||||
|
|
||||||
}, [step, form, accountId]);
|
}, [step, form, accountId]);
|
||||||
|
|
||||||
|
|
||||||
@@ -569,7 +570,13 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
clearStoreDraft(accountId);
|
draftSaveDisabledRef.current = true;
|
||||||
|
clearAllStoreDrafts(accountId);
|
||||||
|
setForm(defaultStoreForm());
|
||||||
|
setFieldErrors({});
|
||||||
|
setSubmitError('');
|
||||||
|
setSmsHint('');
|
||||||
|
setParams({ step: '1' }, { replace: true });
|
||||||
toastSuccess('门店录入成功');
|
toastSuccess('门店录入成功');
|
||||||
navigate(`/stores/${result.store.id}`);
|
navigate(`/stores/${result.store.id}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -73,6 +73,15 @@ export interface PartnerStorePhoneAvailableResponse {
|
|||||||
needConfirm?: boolean;
|
needConfirm?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 合伙人登录前手机号校验 */
|
||||||
|
export interface PartnerPhoneCheckResponse {
|
||||||
|
ok: boolean;
|
||||||
|
maskedPhone: string;
|
||||||
|
name: string;
|
||||||
|
companyName?: string;
|
||||||
|
hasWechat?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PartnerWeeklyReportPeriod {
|
export interface PartnerWeeklyReportPeriod {
|
||||||
startDate: string;
|
startDate: string;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ export class AuthService {
|
|||||||
maskedPhone: this.maskPhone(normalizedPhone),
|
maskedPhone: this.maskPhone(normalizedPhone),
|
||||||
name: account.name,
|
name: account.name,
|
||||||
companyName: primary.companyName,
|
companyName: primary.companyName,
|
||||||
|
hasWechat: !!account.wxOpenId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user