diff --git a/apps/h5-shop/src/contexts/StoreSessionContext.tsx b/apps/h5-shop/src/contexts/StoreSessionContext.tsx index 9d1214c..823ced3 100644 --- a/apps/h5-shop/src/contexts/StoreSessionContext.tsx +++ b/apps/h5-shop/src/contexts/StoreSessionContext.tsx @@ -20,6 +20,9 @@ import { fetchClientConfig, handleShopWechatCallback, handleShopWechatLoginResult, + isShopWechatUnboundError, + SHOP_WX_NEED_PHONE_LOGIN_MSG, + stashShopWechatLoginHint, } from '../lib/wechat-auth'; import { isWechatEnv } from '../lib/weixin'; @@ -83,7 +86,11 @@ export function StoreSessionProvider({ children }: { children: ReactNode }) { } stripOAuthParamsFromLocation(); } - } catch { + } catch (e) { + const raw = e instanceof Error ? e.message : ''; + if (isShopWechatUnboundError(raw)) { + stashShopWechatLoginHint(SHOP_WX_NEED_PHONE_LOGIN_MSG); + } stripOAuthParamsFromLocation(); } } diff --git a/apps/h5-shop/src/lib/wechat-auth.ts b/apps/h5-shop/src/lib/wechat-auth.ts index 562bd3e..a40328a 100644 --- a/apps/h5-shop/src/lib/wechat-auth.ts +++ b/apps/h5-shop/src/lib/wechat-auth.ts @@ -15,6 +15,40 @@ export type ShopAccountProfile = { store?: { name: string }; }; +/** 微信未绑定门店账号时的登录提示 */ +export const SHOP_WX_NEED_PHONE_LOGIN_MSG = '请先用手机号登录'; + +const SHOP_WX_LOGIN_HINT_KEY = 'shop_wx_login_hint'; + +export function isShopWechatUnboundError(message: string): boolean { + return message.includes('首次登录') || message.includes('手机验证码') || message.includes('请先用手机号'); +} + +export function formatShopWechatError(e: unknown): string { + const text = e instanceof Error ? e.message : '微信登录失败'; + if (isShopWechatUnboundError(text)) return SHOP_WX_NEED_PHONE_LOGIN_MSG; + return text; +} + +/** OAuth 在启动层失败时暂存提示,供登录页展示 */ +export function stashShopWechatLoginHint(message: string): void { + try { + sessionStorage.setItem(SHOP_WX_LOGIN_HINT_KEY, message); + } catch { + /* ignore */ + } +} + +export function consumeShopWechatLoginHint(): string | null { + try { + const hint = sessionStorage.getItem(SHOP_WX_LOGIN_HINT_KEY); + if (hint) sessionStorage.removeItem(SHOP_WX_LOGIN_HINT_KEY); + return hint; + } catch { + return null; + } +} + export async function fetchClientConfig(): Promise { return request('SHOP_H5', '/common/client-config'); } diff --git a/apps/h5-shop/src/pages/LoginPage.tsx b/apps/h5-shop/src/pages/LoginPage.tsx index 4316037..7a2088e 100644 --- a/apps/h5-shop/src/pages/LoginPage.tsx +++ b/apps/h5-shop/src/pages/LoginPage.tsx @@ -15,7 +15,9 @@ import { import { routeAfterShopLogin } from './SelectStorePage'; import { bindShopWechatAfterSmsLogin, + consumeShopWechatLoginHint, fetchClientConfig, + formatShopWechatError, handleShopWechatCallback, handleShopWechatLoginResult, loginShopWithWechat, @@ -27,14 +29,6 @@ function maskPhone(phone: string) { return `${phone.slice(0, 3)} **** ${phone.slice(-4)}`; } -function formatWechatError(e: unknown): string { - const text = e instanceof Error ? e.message : '微信登录失败'; - if (text.includes('首次登录') || text.includes('手机验证码')) { - return '该微信尚未绑定门店账号,请先使用手机验证码登录,登录后将自动关联微信'; - } - return text; -} - function ShopAgreementCheckbox({ agreed, onChange, @@ -76,7 +70,7 @@ export default function LoginPage() { const [agreed, setAgreed] = useState(false); const [loading, setLoading] = useState(false); const [wxLoading, setWxLoading] = useState(false); - const [msg, setMsg] = useState(''); + const [msg, setMsg] = useState(() => consumeShopWechatLoginHint() ?? ''); const [codeCooldown, setCodeCooldown] = useState(0); const [wxAuthorize, setWxAuthorize] = useState(false); const agreementRef = useRef(null); @@ -87,6 +81,11 @@ export default function LoginPage() { .catch(() => setWxAuthorize(false)); }, []); + useEffect(() => { + const hint = consumeShopWechatLoginHint(); + if (hint) setMsg(hint); + }, []); + useEffect(() => { if (!isWechatEnv() || !wxAuthorize || !params.get('code')) return; void handleShopWechatCallback() @@ -100,7 +99,7 @@ export default function LoginPage() { routeAfterShopLogin(session, navigate); } }) - .catch((e) => setMsg(formatWechatError(e))); + .catch((e) => setMsg(formatShopWechatError(e))); }, [applySession, navigate, params, setSearchParams, wxAuthorize]); const quickStoreName = savedProfile?.storeName ?? '门店管理中心'; @@ -178,7 +177,7 @@ export default function LoginPage() { routeAfterShopLogin(session, navigate); } } catch (e) { - setMsg(formatWechatError(e)); + setMsg(formatShopWechatError(e)); } finally { setWxLoading(false); } diff --git a/server/dukang-api/src/modules/iam/auth.service.ts b/server/dukang-api/src/modules/iam/auth.service.ts index e3cff41..512b414 100644 --- a/server/dukang-api/src/modules/iam/auth.service.ts +++ b/server/dukang-api/src/modules/iam/auth.service.ts @@ -1538,7 +1538,7 @@ export class AuthService { }); if (!account) { - throw new BadRequestException('首次登录请使用手机验证码,登录后将自动关联微信'); + throw new BadRequestException('请先用手机号登录'); } account = await this.prisma.storeAccount.update({