This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { uploadFileToOss, type OssMediaType } from '../lib/upload';
|
import { uploadFileToOss, type OssMediaType } from '../lib/upload';
|
||||||
import { enqueueUpload } from '../lib/upload-lock';
|
import { enqueueUpload } from '../lib/upload-lock';
|
||||||
|
import { formatChooseImageFailMessage } from '@dukang/weixin-sdk';
|
||||||
|
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||||
import { toastError } from '../lib/toast';
|
import { toastError } from '../lib/toast';
|
||||||
|
|
||||||
type OssUploadFieldProps = {
|
type OssUploadFieldProps = {
|
||||||
@@ -12,10 +14,27 @@ type OssUploadFieldProps = {
|
|||||||
wide?: boolean;
|
wide?: boolean;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
/** 兼容现有调用:选图不再依赖 openId 绑定 */
|
||||||
|
wechatReady?: boolean;
|
||||||
|
onWechatReadyChange?: (ready: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_MAX_MB = 10;
|
const DEFAULT_MAX_MB = 10;
|
||||||
|
|
||||||
|
function formatWechatUploadError(e: unknown): string {
|
||||||
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
|
const formatted = formatChooseImageFailMessage(msg);
|
||||||
|
if (formatted) return formatted;
|
||||||
|
if (/invalid signature/i.test(msg)) {
|
||||||
|
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名,并刷新页面后重试';
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acceptsImages(accept: string) {
|
||||||
|
return accept.includes('image');
|
||||||
|
}
|
||||||
|
|
||||||
export default function OssUploadField({
|
export default function OssUploadField({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -29,6 +48,7 @@ export default function OssUploadField({
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [showAlbumFallback, setShowAlbumFallback] = useState(false);
|
||||||
|
|
||||||
function showUploadError(text: string) {
|
function showUploadError(text: string) {
|
||||||
setError(text);
|
setError(text);
|
||||||
@@ -37,6 +57,17 @@ export default function OssUploadField({
|
|||||||
|
|
||||||
const resolvedAccept =
|
const resolvedAccept =
|
||||||
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? 'image/*,.pdf' : 'image/*');
|
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? 'image/*,.pdf' : 'image/*');
|
||||||
|
const inWechat = isWechatEnv();
|
||||||
|
const useWechatPicker =
|
||||||
|
inWechat && (mediaType === 'IMAGE' || (mediaType === 'FILE' && acceptsImages(resolvedAccept)));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!useWechatPicker) return;
|
||||||
|
weixinSdk.reset();
|
||||||
|
void weixinSdk.init().catch(() => {
|
||||||
|
/* 点击上传时会再次初始化 */
|
||||||
|
});
|
||||||
|
}, [useWechatPicker]);
|
||||||
|
|
||||||
async function persistUpload(file: File) {
|
async function persistUpload(file: File) {
|
||||||
if (!file.size) {
|
if (!file.size) {
|
||||||
@@ -64,21 +95,60 @@ export default function OssUploadField({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickFile() {
|
async function pickWechatImage() {
|
||||||
|
setUploading(true);
|
||||||
|
setError('');
|
||||||
|
try {
|
||||||
|
weixinSdk.reset();
|
||||||
|
await weixinSdk.init();
|
||||||
|
// 选图 + 上传须在同一个队列任务内完成,避免嵌套 enqueueUpload 死锁
|
||||||
|
await enqueueUpload(async () => {
|
||||||
|
const files = await weixinSdk.chooseImages({
|
||||||
|
count: 1,
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
});
|
||||||
|
if (!files?.[0]) return;
|
||||||
|
await persistUpload(files[0]);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
|
if (/cancel/i.test(msg)) return;
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
setUploading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pickFile() {
|
||||||
if (uploading) return;
|
if (uploading) return;
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
|
if (useWechatPicker) {
|
||||||
|
try {
|
||||||
|
await pickWechatImage();
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
|
if (/cancel/i.test(msg)) return;
|
||||||
|
const formatted = formatWechatUploadError(e);
|
||||||
|
setError(`${formatted},可改从系统相册选择`);
|
||||||
|
setShowAlbumFallback(true);
|
||||||
|
inputRef.current?.click();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inputRef.current?.click();
|
inputRef.current?.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
const isImage = mediaType === 'IMAGE' && value;
|
const isImage = mediaType === 'IMAGE' && value;
|
||||||
const isFile = mediaType === 'FILE' && value;
|
const isFile = mediaType === 'FILE' && value;
|
||||||
const busy = uploading;
|
const busy = uploading;
|
||||||
const pickerLabel = label ?? (mediaType === 'IMAGE' ? '从系统相册选择' : '点击上传');
|
const pickerLabel = label ?? (useWechatPicker ? '拍照 / 从相册选择' : '点击上传');
|
||||||
|
|
||||||
const triggerProps = {
|
const triggerProps = {
|
||||||
type: 'button' as const,
|
type: 'button' as const,
|
||||||
disabled: busy,
|
disabled: busy,
|
||||||
onClick: pickFile,
|
onClick: () => void pickFile(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -92,6 +162,7 @@ export default function OssUploadField({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
|
setShowAlbumFallback(false);
|
||||||
void uploadSelectedFile(file);
|
void uploadSelectedFile(file);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -120,13 +191,24 @@ export default function OssUploadField({
|
|||||||
className={`partner-upload-dashed${wide ? ' partner-upload-dashed--wide' : ''}${compact ? ' partner-upload-dashed--compact' : ''}`}
|
className={`partner-upload-dashed${wide ? ' partner-upload-dashed--wide' : ''}${compact ? ' partner-upload-dashed--compact' : ''}`}
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined text-primary" style={{ fontSize: compact ? 28 : 36 }}>
|
<span className="material-symbols-outlined text-primary" style={{ fontSize: compact ? 28 : 36 }}>
|
||||||
{busy ? 'hourglass_top' : 'photo_library'}
|
{busy ? 'hourglass_top' : 'add_a_photo'}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-primary" style={{ fontWeight: 500 }}>
|
<span className="text-primary" style={{ fontWeight: 500 }}>
|
||||||
{uploading ? '上传中…' : pickerLabel}
|
{uploading ? '上传中…' : pickerLabel}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{showAlbumFallback && useWechatPicker && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="partner-btn-outline"
|
||||||
|
style={{ marginTop: 8, width: '100%' }}
|
||||||
|
disabled={busy}
|
||||||
|
onClick={() => inputRef.current?.click()}
|
||||||
|
>
|
||||||
|
从系统相册选择
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{error && <p className="partner-form-error" role="alert">{error}</p>}
|
{error && <p className="partner-form-error" role="alert">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { checkStorePhoneAvailable } from '../lib/storePhone';
|
|||||||
|
|
||||||
import { fetchPartnerCities, type OpenCityOption } from '../lib/upload';
|
import { fetchPartnerCities, type OpenCityOption } from '../lib/upload';
|
||||||
|
|
||||||
|
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||||
|
|
||||||
import { usePartnerSession } from '../contexts/PartnerSessionContext';
|
import { usePartnerSession } from '../contexts/PartnerSessionContext';
|
||||||
|
|
||||||
import TencentLocPickerOverlay from '../components/TencentLocPickerOverlay';
|
import TencentLocPickerOverlay from '../components/TencentLocPickerOverlay';
|
||||||
@@ -130,7 +132,13 @@ export default function StoreCreatePage() {
|
|||||||
saveStoreDraft({ step, form }, accountId);
|
saveStoreDraft({ step, form }, accountId);
|
||||||
}, [step, form, accountId]);
|
}, [step, form, accountId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step !== 2 || !isWechatEnv()) return;
|
||||||
|
weixinSdk.reset();
|
||||||
|
void weixinSdk.init().catch(() => {
|
||||||
|
/* OssUploadField 点击时会再次初始化 */
|
||||||
|
});
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
@@ -900,7 +908,7 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
onChange={(coverUrl) => patchForm({ coverUrl })}
|
onChange={(coverUrl) => patchForm({ coverUrl })}
|
||||||
|
|
||||||
label="从系统相册选择"
|
label="拍照 / 从相册选择"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -946,7 +954,7 @@ export default function StoreCreatePage() {
|
|||||||
|
|
||||||
<h3 className="headline-md">签约合同 <span className="text-primary">*</span></h3>
|
<h3 className="headline-md">签约合同 <span className="text-primary">*</span></h3>
|
||||||
|
|
||||||
<p className="label-md text-muted" style={{ margin: '4px 0 12px' }}>上传签约协议首页与盖章页</p>
|
<p className="label-md text-muted" style={{ margin: '4px 0 12px' }}>拍照上传签约协议首页与盖章页</p>
|
||||||
|
|
||||||
<OssUploadField
|
<OssUploadField
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ export default function StoreDetailPage() {
|
|||||||
mediaType="IMAGE"
|
mediaType="IMAGE"
|
||||||
value={coverUrl}
|
value={coverUrl}
|
||||||
onChange={setCoverUrl}
|
onChange={setCoverUrl}
|
||||||
label="从系统相册选择"
|
label="拍照 / 从相册选择"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import LoginPage from './pages/LoginPage';
|
|||||||
import LegalPage from './pages/LegalPage';
|
import LegalPage from './pages/LegalPage';
|
||||||
import SelectStorePage from './pages/SelectStorePage';
|
import SelectStorePage from './pages/SelectStorePage';
|
||||||
import HomePage from './pages/HomePage';
|
import HomePage from './pages/HomePage';
|
||||||
|
import RedeemConfirmPage from './pages/RedeemConfirmPage';
|
||||||
import PhoneRedeemPage from './pages/PhoneRedeemPage';
|
import PhoneRedeemPage from './pages/PhoneRedeemPage';
|
||||||
import RedeemSuccessPage from './pages/RedeemSuccessPage';
|
import RedeemSuccessPage from './pages/RedeemSuccessPage';
|
||||||
import RecordsPage from './pages/RecordsPage';
|
import RecordsPage from './pages/RecordsPage';
|
||||||
@@ -21,7 +22,7 @@ export default function App() {
|
|||||||
<Route path="/legal/privacy-policy" element={<LegalPage docId="privacy-policy" />} />
|
<Route path="/legal/privacy-policy" element={<LegalPage docId="privacy-policy" />} />
|
||||||
<Route path="/select-store" element={<SelectStorePage />} />
|
<Route path="/select-store" element={<SelectStorePage />} />
|
||||||
<Route path="/staff" element={<StaffPage />} />
|
<Route path="/staff" element={<StaffPage />} />
|
||||||
<Route path="/redeem" element={<Navigate to="/redeem/phone" replace />} />
|
<Route path="/redeem" element={<RedeemConfirmPage />} />
|
||||||
<Route path="/redeem/phone" element={<PhoneRedeemPage />} />
|
<Route path="/redeem/phone" element={<PhoneRedeemPage />} />
|
||||||
<Route path="/redeem/success" element={<RedeemSuccessPage />} />
|
<Route path="/redeem/success" element={<RedeemSuccessPage />} />
|
||||||
<Route element={<TabLayout />}>
|
<Route element={<TabLayout />}>
|
||||||
|
|||||||
@@ -1,15 +1,47 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
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 PullToRefresh from '@dukang/shared-ui/PullToRefresh';
|
||||||
|
import { useStoreSession } from '../contexts/StoreSessionContext';
|
||||||
import { request } from '../lib/api';
|
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) {
|
function formatMoney(n: number) {
|
||||||
return n.toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 });
|
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() {
|
export default function HomePage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { applySession } = useStoreSession();
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [dash, setDash] = useState<Record<string, unknown> | null>(null);
|
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(() => {
|
const loadDashboard = useCallback(() => {
|
||||||
return request<Record<string, unknown>>('SHOP_H5', '/shop/dashboard')
|
return request<Record<string, unknown>>('SHOP_H5', '/shop/dashboard')
|
||||||
@@ -25,6 +57,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onResume() {
|
function onResume() {
|
||||||
|
setScanning(false);
|
||||||
void loadDashboard();
|
void loadDashboard();
|
||||||
}
|
}
|
||||||
function onVisibility() {
|
function onVisibility() {
|
||||||
@@ -40,6 +73,88 @@ export default function HomePage() {
|
|||||||
};
|
};
|
||||||
}, [loadDashboard]);
|
}, [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 store = dash?.store as Record<string, unknown> | undefined;
|
||||||
const recent = (dash?.recentRecords as Array<Record<string, unknown>>) || [];
|
const recent = (dash?.recentRecords as Array<Record<string, unknown>>) || [];
|
||||||
const status = String(store?.status || '');
|
const status = String(store?.status || '');
|
||||||
@@ -79,13 +194,20 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="shop-home-scan">
|
<section className="shop-home-scan">
|
||||||
<Link
|
<button
|
||||||
to="/redeem/phone"
|
type="button"
|
||||||
className="shop-home-scan-btn"
|
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>
|
<span className="material-symbols-outlined">smartphone</span>
|
||||||
|
手机号核销
|
||||||
</Link>
|
</Link>
|
||||||
<p className="shop-home-scan-label">手机号核销</p>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="shop-home-status">
|
<section className="shop-home-status">
|
||||||
@@ -132,6 +254,17 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<WechatScanAuthModal
|
||||||
|
open={authModalOpen}
|
||||||
|
loading={authLoading}
|
||||||
|
error={authError}
|
||||||
|
onAuthorize={() => void startWechatAuth()}
|
||||||
|
onCancel={() => {
|
||||||
|
setAuthModalOpen(false);
|
||||||
|
setAuthError('');
|
||||||
|
sessionStorage.removeItem(PENDING_SCAN_KEY);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</PullToRefresh>
|
</PullToRefresh>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
|
import Taro from '@tarojs/taro';
|
||||||
import PageShell from '../../components/PageShell';
|
import PageShell from '../../components/PageShell';
|
||||||
import SubPageHeader from '../../components/SubPageHeader';
|
import SubPageHeader from '../../components/SubPageHeader';
|
||||||
import { request } from '../../lib/api';
|
import { request } from '../../lib/api';
|
||||||
@@ -28,7 +29,17 @@ export default function BenefitDetailPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageShell variant="sub" className="benefit-detail-page">
|
<PageShell variant="sub" className="benefit-detail-page">
|
||||||
<SubPageHeader title="权益明细" />
|
<SubPageHeader
|
||||||
|
title="权益明细"
|
||||||
|
onBack={() => {
|
||||||
|
const pages = Taro.getCurrentPages();
|
||||||
|
if (pages.length > 1) {
|
||||||
|
Taro.navigateBack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Taro.switchTab({ url: '/pages/mine/index' });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<View className="sub-page-body">
|
<View className="sub-page-body">
|
||||||
<View className="order-card">
|
<View className="order-card">
|
||||||
{loading ? <View className="u-empty">加载中…</View> : null}
|
{loading ? <View className="u-empty">加载中…</View> : null}
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ export default function MinePage() {
|
|||||||
<Text className="mine-card-title">我的资产</Text>
|
<Text className="mine-card-title">我的资产</Text>
|
||||||
<Text
|
<Text
|
||||||
className="mine-card-link"
|
className="mine-card-link"
|
||||||
onClick={() => Taro.switchTab({ url: '/pages/benefit/index' })}
|
onClick={() => Taro.navigateTo({ url: '/pages/benefit-detail/index' })}
|
||||||
>
|
>
|
||||||
查看明细 ›
|
查看明细 ›
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user