合伙人端和门店端微信SDK调用相机复原
This commit is contained in:
@@ -1,15 +1,47 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
|
||||
import { useStoreSession } from '../contexts/StoreSessionContext';
|
||||
import { request } from '../lib/api';
|
||||
import { parseRedeemTokenFromScan } from '../lib/redeem-scan';
|
||||
import {
|
||||
authorizeShopWechat,
|
||||
checkNeedsWechatAuth,
|
||||
fetchShopAccount,
|
||||
handleShopWechatCallback,
|
||||
handleShopWechatLoginResult,
|
||||
} from '../lib/wechat-auth';
|
||||
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||
import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk';
|
||||
import WechatScanAuthModal from '../components/WechatScanAuthModal';
|
||||
|
||||
const PENDING_SCAN_KEY = 'shop_pending_scan';
|
||||
|
||||
function formatMoney(n: number) {
|
||||
return n.toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
function formatScanError(e: unknown): string {
|
||||
const msg = e instanceof Error ? e.message : '扫码失败,请重试';
|
||||
if (/invalid signature/i.test(msg)) {
|
||||
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名,并刷新页面后重试';
|
||||
}
|
||||
if (/offline verifying|权限验证中|接口未就绪/i.test(msg)) {
|
||||
return `${msg}(可在 URL 后加 ?wxdebug=1 开启 JSSDK 调试查看详情)`;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
export default function HomePage() {
|
||||
const navigate = useNavigate();
|
||||
const { applySession } = useStoreSession();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [dash, setDash] = useState<Record<string, unknown> | null>(null);
|
||||
const [scanMsg, setScanMsg] = useState('');
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [authModalOpen, setAuthModalOpen] = useState(false);
|
||||
const [authLoading, setAuthLoading] = useState(false);
|
||||
const [authError, setAuthError] = useState('');
|
||||
|
||||
const loadDashboard = useCallback(() => {
|
||||
return request<Record<string, unknown>>('SHOP_H5', '/shop/dashboard')
|
||||
@@ -25,6 +57,7 @@ export default function HomePage() {
|
||||
|
||||
useEffect(() => {
|
||||
function onResume() {
|
||||
setScanning(false);
|
||||
void loadDashboard();
|
||||
}
|
||||
function onVisibility() {
|
||||
@@ -40,6 +73,88 @@ export default function HomePage() {
|
||||
};
|
||||
}, [loadDashboard]);
|
||||
|
||||
async function runScan() {
|
||||
if (!isWechatEnv()) {
|
||||
setScanMsg('请在微信内打开门店端进行扫码核销');
|
||||
return;
|
||||
}
|
||||
setScanning(true);
|
||||
setScanMsg('');
|
||||
try {
|
||||
await weixinSdk.init();
|
||||
const raw = await weixinSdk.scanQrCode();
|
||||
if (!raw) {
|
||||
void loadDashboard();
|
||||
return;
|
||||
}
|
||||
const token = parseRedeemTokenFromScan(raw);
|
||||
if (!token) {
|
||||
setScanMsg('无法识别核销码,请扫描用户出示的核销二维码');
|
||||
return;
|
||||
}
|
||||
navigate(`/redeem?token=${encodeURIComponent(token)}`);
|
||||
} catch (e) {
|
||||
setScanMsg(formatScanError(e));
|
||||
} finally {
|
||||
setScanning(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWechatEnv() || !searchParams.get('code')) return;
|
||||
void handleShopWechatCallback()
|
||||
.then((result) => {
|
||||
if (!result) return;
|
||||
const session = handleShopWechatLoginResult(result);
|
||||
if (session) {
|
||||
applySession(session);
|
||||
}
|
||||
setAuthModalOpen(false);
|
||||
setAuthError('');
|
||||
stripOAuthParamsFromLocation();
|
||||
setSearchParams({}, { replace: true });
|
||||
const shouldScan = sessionStorage.getItem(PENDING_SCAN_KEY) === '1';
|
||||
sessionStorage.removeItem(PENDING_SCAN_KEY);
|
||||
if (shouldScan) {
|
||||
window.setTimeout(() => void runScan(), 0);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
setAuthError(e instanceof Error ? e.message : '微信授权失败');
|
||||
});
|
||||
}, [searchParams, applySession, setSearchParams]);
|
||||
|
||||
async function handleScan() {
|
||||
setScanMsg('');
|
||||
if (!isWechatEnv()) {
|
||||
setScanMsg('请在微信内打开门店端进行扫码核销');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const profile = await fetchShopAccount();
|
||||
if (await checkNeedsWechatAuth(profile)) {
|
||||
setAuthModalOpen(true);
|
||||
return;
|
||||
}
|
||||
await runScan();
|
||||
} catch (e) {
|
||||
setScanMsg(e instanceof Error ? e.message : '无法发起扫码');
|
||||
}
|
||||
}
|
||||
|
||||
async function startWechatAuth() {
|
||||
setAuthLoading(true);
|
||||
setAuthError('');
|
||||
try {
|
||||
sessionStorage.setItem(PENDING_SCAN_KEY, '1');
|
||||
await authorizeShopWechat();
|
||||
} catch (e) {
|
||||
sessionStorage.removeItem(PENDING_SCAN_KEY);
|
||||
setAuthError(e instanceof Error ? e.message : '微信授权失败');
|
||||
setAuthLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const store = dash?.store as Record<string, unknown> | undefined;
|
||||
const recent = (dash?.recentRecords as Array<Record<string, unknown>>) || [];
|
||||
const status = String(store?.status || '');
|
||||
@@ -79,13 +194,20 @@ export default function HomePage() {
|
||||
</section>
|
||||
|
||||
<section className="shop-home-scan">
|
||||
<Link
|
||||
to="/redeem/phone"
|
||||
<button
|
||||
type="button"
|
||||
className="shop-home-scan-btn"
|
||||
disabled={scanning}
|
||||
onClick={() => void handleScan()}
|
||||
>
|
||||
<span className="material-symbols-outlined">qr_code_scanner</span>
|
||||
</button>
|
||||
<p className="shop-home-scan-label">{scanning ? '正在打开相机…' : '扫码核销'}</p>
|
||||
{scanMsg && <p className="shop-home-scan-msg" role="alert">{scanMsg}</p>}
|
||||
<Link to="/redeem/phone" className="shop-home-phone-link">
|
||||
<span className="material-symbols-outlined">smartphone</span>
|
||||
手机号核销
|
||||
</Link>
|
||||
<p className="shop-home-scan-label">手机号核销</p>
|
||||
</section>
|
||||
|
||||
<section className="shop-home-status">
|
||||
@@ -132,6 +254,17 @@ export default function HomePage() {
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<WechatScanAuthModal
|
||||
open={authModalOpen}
|
||||
loading={authLoading}
|
||||
error={authError}
|
||||
onAuthorize={() => void startWechatAuth()}
|
||||
onCancel={() => {
|
||||
setAuthModalOpen(false);
|
||||
setAuthError('');
|
||||
sessionStorage.removeItem(PENDING_SCAN_KEY);
|
||||
}}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user