feat: 技术支持工单/企微权限/开发版本管理/消息推送等迭代

This commit is contained in:
2026-08-04 21:32:13 +08:00
parent c8ea5a3119
commit 9d96c73246
1341 changed files with 0 additions and 195605 deletions
-194
View File
@@ -1,194 +0,0 @@
import { useCallback, useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk';
import { useStoreSession } from '../contexts/StoreSessionContext';
import { getStoreProfile, request } from '../lib/api';
import {
authorizeShopWechat,
fetchClientConfig,
fetchShopAccount,
handleShopWechatCallback,
handleShopWechatLoginResult,
} from '../lib/wechat-auth';
import { isWechatEnv } from '../lib/weixin';
import { useStorePageView } from '../lib/usePageView';
export default function MinePage() {
useStorePageView('store_mine_view');
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const { resetSession, store: sessionStore, applySession } = useStoreSession();
const profile = sessionStore ?? getStoreProfile();
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('');
const loadMine = useCallback(() => {
return Promise.all([
request('SHOP_H5', '/shop/store').then(setStore).catch(() => setStore(null)),
fetchClientConfig()
.then((config) => setWxAuthorize(isWxAuthorizeEnabled(config)))
.catch(() => setWxAuthorize(false)),
fetchShopAccount()
.then((me) => setHasWechat(!!(me.hasWechat || me.wxOpenId)))
.catch(() => setHasWechat(null)),
]);
}, []);
useEffect(() => {
void loadMine();
}, [loadMine]);
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 hoursParts: string[] = [];
if (store?.openTime && store?.closeTime) hoursParts.push(`${store.openTime} - ${store.closeTime}`);
if (store?.openTime2 && store?.closeTime2) hoursParts.push(`${store.openTime2} - ${store.closeTime2}`);
const hoursText = hoursParts.length ? hoursParts.join('') : '09:30 - 22:00';
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 (
<PullToRefresh onRefresh={loadMine} className="shop-mine-page">
<header className="shop-mine-header">
<h1 className="app-page-title"></h1>
</header>
<div className="shop-mine-content">
<h3 className="shop-mine-section-title"></h3>
<div className="shop-mine-info-card">
<div className="shop-mine-info-row">
<div>
<p className="shop-mine-info-label"></p>
<p className="shop-mine-info-value name">{String(store?.name || profile?.storeName || '—')}</p>
</div>
<span className="material-symbols-outlined shop-mine-lock">lock</span>
</div>
<div className="shop-mine-info-row">
<div>
<p className="shop-mine-info-label"></p>
<p className="shop-mine-info-value">{String(store?.address || '—')}</p>
</div>
<span className="material-symbols-outlined shop-mine-lock">lock</span>
</div>
<div className="shop-mine-info-row">
<div>
<p className="shop-mine-info-label"></p>
<p className="shop-mine-info-value">{String(store?.phone || profile?.phone || '—')}</p>
</div>
<span className="material-symbols-outlined shop-mine-lock">lock</span>
</div>
<div className="shop-mine-info-row">
<div>
<p className="shop-mine-info-label"></p>
<p className="shop-mine-info-value">{hoursText}</p>
</div>
<span className="material-symbols-outlined shop-mine-lock">lock</span>
</div>
</div>
<div className="shop-mine-actions">
{multiStore ? (
<button type="button" className="shop-mine-action" onClick={() => navigate('/select-store')}>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>swap_horiz</span>
</button>
) : null}
<button type="button" className="shop-mine-action" onClick={() => navigate('/withdraw')}>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>account_balance_wallet</span>
</button>
{profile?.isPrimary ? (
<button type="button" className="shop-mine-action" onClick={() => navigate('/packages')}>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>restaurant_menu</span>
</button>
) : null}
{profile?.isPrimary ? (
<button type="button" className="shop-mine-action" onClick={() => navigate('/staff')}>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>group</span>
</button>
) : null}
</div>
<div className="shop-mine-help">
<span className="material-symbols-outlined" style={{ fontSize: 16 }}>help</span>
<p></p>
</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
type="button"
className="shop-mine-logout"
onClick={() => { resetSession(); navigate('/login'); }}
>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>logout</span>
退
</button>
</div>
</PullToRefresh>
);
}