@@ -9,6 +9,8 @@ export type ShopAccountProfile = {
|
|||||||
storeId: string;
|
storeId: string;
|
||||||
name: string;
|
name: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
|
/** 是否已绑定微信(来自 /shop/auth/me account.hasWechat) */
|
||||||
|
hasWechat?: boolean;
|
||||||
wxOpenId?: string | null;
|
wxOpenId?: string | null;
|
||||||
store?: { name: string };
|
store?: { name: string };
|
||||||
};
|
};
|
||||||
@@ -18,7 +20,27 @@ export async function fetchClientConfig(): Promise<ClientRuntimeConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchShopAccount(): Promise<ShopAccountProfile> {
|
export async function fetchShopAccount(): Promise<ShopAccountProfile> {
|
||||||
return request<ShopAccountProfile>('SHOP_H5', '/shop/auth/me');
|
const me = await request<{
|
||||||
|
id?: string;
|
||||||
|
storeId?: string;
|
||||||
|
name?: string;
|
||||||
|
phone?: string;
|
||||||
|
account?: {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
phone?: string;
|
||||||
|
hasWechat?: boolean;
|
||||||
|
};
|
||||||
|
store?: { name?: string } | null;
|
||||||
|
}>('SHOP_H5', '/shop/auth/me');
|
||||||
|
return {
|
||||||
|
id: String(me.account?.id ?? me.id ?? ''),
|
||||||
|
storeId: String(me.storeId ?? ''),
|
||||||
|
name: String(me.account?.name ?? me.name ?? ''),
|
||||||
|
phone: String(me.account?.phone ?? me.phone ?? ''),
|
||||||
|
hasWechat: !!me.account?.hasWechat,
|
||||||
|
store: me.store?.name ? { name: String(me.store.name) } : undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function needsWechatAuth(
|
export function needsWechatAuth(
|
||||||
@@ -26,7 +48,8 @@ export function needsWechatAuth(
|
|||||||
config?: Pick<ClientRuntimeConfig, 'wxAuthorize'> | null,
|
config?: Pick<ClientRuntimeConfig, 'wxAuthorize'> | null,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (config && !isWxAuthorizeEnabled(config)) return false;
|
if (config && !isWxAuthorizeEnabled(config)) return false;
|
||||||
return isWechatEnv() && !!profile && !profile.wxOpenId;
|
const unbound = !!profile && !(profile.hasWechat || profile.wxOpenId);
|
||||||
|
return isWechatEnv() && unbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkNeedsWechatAuth(profile: ShopAccountProfile | null): Promise<boolean> {
|
export async function checkNeedsWechatAuth(profile: ShopAccountProfile | null): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,21 +1,95 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
|
||||||
|
import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk';
|
||||||
import { useStoreSession } from '../contexts/StoreSessionContext';
|
import { useStoreSession } from '../contexts/StoreSessionContext';
|
||||||
import { getStoreProfile, request } from '../lib/api';
|
import { getStoreProfile, request } from '../lib/api';
|
||||||
|
import {
|
||||||
|
authorizeShopWechat,
|
||||||
|
fetchClientConfig,
|
||||||
|
fetchShopAccount,
|
||||||
|
handleShopWechatCallback,
|
||||||
|
handleShopWechatLoginResult,
|
||||||
|
} from '../lib/wechat-auth';
|
||||||
|
import { isWechatEnv } from '../lib/weixin';
|
||||||
|
|
||||||
export default function MinePage() {
|
export default function MinePage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { resetSession, store: sessionStore } = useStoreSession();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const { resetSession, store: sessionStore, applySession } = useStoreSession();
|
||||||
const profile = sessionStore ?? getStoreProfile();
|
const profile = sessionStore ?? getStoreProfile();
|
||||||
const [store, setStore] = useState<Record<string, unknown> | null>(null);
|
const [store, setStore] = useState<Record<string, unknown> | null>(null);
|
||||||
|
const [hasWechat, setHasWechat] = useState<boolean | null>(null);
|
||||||
|
const [wxAuthorize, setWxAuthorize] = useState(false);
|
||||||
|
const [binding, setBinding] = useState(false);
|
||||||
|
const [bindMsg, setBindMsg] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
request('SHOP_H5', '/shop/store').then(setStore).catch(() => setStore(null));
|
request('SHOP_H5', '/shop/store').then(setStore).catch(() => setStore(null));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchClientConfig()
|
||||||
|
.then((config) => setWxAuthorize(isWxAuthorizeEnabled(config)))
|
||||||
|
.catch(() => setWxAuthorize(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchShopAccount()
|
||||||
|
.then((me) => setHasWechat(!!(me.hasWechat || me.wxOpenId)))
|
||||||
|
.catch(() => setHasWechat(null));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isWechatEnv() || !wxAuthorize || !searchParams.get('code')) return;
|
||||||
|
void handleShopWechatCallback()
|
||||||
|
.then((result) => {
|
||||||
|
if (!result) return;
|
||||||
|
const session = handleShopWechatLoginResult(result);
|
||||||
|
if (session) {
|
||||||
|
applySession(session);
|
||||||
|
setHasWechat(true);
|
||||||
|
setBindMsg('微信绑定成功');
|
||||||
|
}
|
||||||
|
stripOAuthParamsFromLocation();
|
||||||
|
setSearchParams({}, { replace: true });
|
||||||
|
})
|
||||||
|
.catch((e) => setBindMsg(e instanceof Error ? e.message : '微信绑定失败'));
|
||||||
|
}, [applySession, searchParams, setSearchParams, wxAuthorize]);
|
||||||
|
|
||||||
const openTime = String(store?.openTime || '09:30');
|
const openTime = String(store?.openTime || '09:30');
|
||||||
const closeTime = String(store?.closeTime || '22:00');
|
const closeTime = String(store?.closeTime || '22:00');
|
||||||
const multiStore = (profile?.stores?.length ?? 0) > 1;
|
const multiStore = (profile?.stores?.length ?? 0) > 1;
|
||||||
|
const showBindWechat = wxAuthorize && hasWechat === false;
|
||||||
|
|
||||||
|
async function bindWechat() {
|
||||||
|
if (binding) return;
|
||||||
|
setBindMsg('');
|
||||||
|
if (!isWechatEnv()) {
|
||||||
|
setBindMsg('请在微信内打开门店端以绑定微信');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setBinding(true);
|
||||||
|
try {
|
||||||
|
const result = await authorizeShopWechat();
|
||||||
|
if (!result) {
|
||||||
|
// 跳转公众号 OAuth,回跳后由上面 useEffect 处理
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const session = handleShopWechatLoginResult(result);
|
||||||
|
if (session) {
|
||||||
|
applySession(session);
|
||||||
|
setHasWechat(true);
|
||||||
|
setBindMsg('微信绑定成功');
|
||||||
|
} else {
|
||||||
|
setBindMsg('绑定未完成,请重试');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setBindMsg(e instanceof Error ? e.message : '微信绑定失败');
|
||||||
|
} finally {
|
||||||
|
setBinding(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shop-mine-page">
|
<div className="shop-mine-page">
|
||||||
@@ -77,6 +151,20 @@ export default function MinePage() {
|
|||||||
<p>如需修改信息请联系城市合伙人</p>
|
<p>如需修改信息请联系城市合伙人</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{bindMsg ? <p className="shop-mine-bind-msg">{bindMsg}</p> : null}
|
||||||
|
|
||||||
|
{showBindWechat ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="shop-mine-bind-wechat"
|
||||||
|
disabled={binding}
|
||||||
|
onClick={() => void bindWechat()}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>chat</span>
|
||||||
|
{binding ? '绑定中…' : '绑定微信'}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="shop-mine-logout"
|
className="shop-mine-logout"
|
||||||
|
|||||||
@@ -2153,8 +2153,38 @@
|
|||||||
color: var(--color-subtle-gray);
|
color: var(--color-subtle-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shop-mine-bind-msg {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-heritage-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-mine-bind-wechat {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: #07c160;
|
||||||
|
color: #fff;
|
||||||
|
font-family: var(--font-headline);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-mine-bind-wechat:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.shop-mine-logout {
|
.shop-mine-logout {
|
||||||
margin-top: 24px;
|
margin-top: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border: 1px solid var(--color-heritage-red);
|
border: 1px solid var(--color-heritage-red);
|
||||||
|
|||||||
Reference in New Issue
Block a user