feat(h5): add pull-to-refresh on user partner and shop pages
CI / verify (pull_request) Has been cancelled

Share PullToRefresh for partner/shop H5; enable Taro pull-down on mini-user lists.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-22 11:19:42 +08:00
parent 270cebc79a
commit ae7c63c08d
29 changed files with 443 additions and 121 deletions
+3 -2
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
import { request } from '../lib/api';
function formatMoney(n: number) {
@@ -47,7 +48,7 @@ export default function HomePage() {
const closeTime = String(store?.closeTime || '22:00');
return (
<div className="shop-home-page">
<PullToRefresh onRefresh={loadDashboard} className="shop-home-page">
<header className="shop-home-header">
<h1 className="app-page-title"></h1>
</header>
@@ -127,6 +128,6 @@ export default function HomePage() {
</section>
</div>
</div>
</PullToRefresh>
);
}
+16 -15
View File
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
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';
@@ -24,21 +25,21 @@ export default function MinePage() {
const [binding, setBinding] = useState(false);
const [bindMsg, setBindMsg] = useState('');
useEffect(() => {
request('SHOP_H5', '/shop/store').then(setStore).catch(() => setStore(null));
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(() => {
fetchClientConfig()
.then((config) => setWxAuthorize(isWxAuthorizeEnabled(config)))
.catch(() => setWxAuthorize(false));
}, []);
useEffect(() => {
fetchShopAccount()
.then((me) => setHasWechat(!!(me.hasWechat || me.wxOpenId)))
.catch(() => setHasWechat(null));
}, []);
void loadMine();
}, [loadMine]);
useEffect(() => {
if (!isWechatEnv() || !wxAuthorize || !searchParams.get('code')) return;
@@ -92,7 +93,7 @@ export default function MinePage() {
}
return (
<div className="shop-mine-page">
<PullToRefresh onRefresh={loadMine} className="shop-mine-page">
<header className="shop-mine-header">
<h1 className="app-page-title"></h1>
</header>
@@ -174,6 +175,6 @@ export default function MinePage() {
退
</button>
</div>
</div>
</PullToRefresh>
);
}
+17 -10
View File
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
import { request } from '../lib/api';
type RangeKey = 'today' | '7d' | '30d';
@@ -28,15 +29,21 @@ export default function RecordsPage() {
const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');
const [storeName, setStoreName] = useState('');
useEffect(() => {
request<{ list: Array<Record<string, unknown>> }>('SHOP_H5', '/shop/redeem/records').then((d) => {
setRecords(d.list || []);
});
request<Record<string, unknown>>('SHOP_H5', '/shop/store')
.then((s) => setStoreName(String(s.name || '')))
.catch(() => {});
const loadRecords = useCallback(() => {
return Promise.all([
request<{ list: Array<Record<string, unknown>> }>('SHOP_H5', '/shop/redeem/records').then((d) => {
setRecords(d.list || []);
}),
request<Record<string, unknown>>('SHOP_H5', '/shop/store')
.then((s) => setStoreName(String(s.name || '')))
.catch(() => {}),
]);
}, []);
useEffect(() => {
void loadRecords();
}, [loadRecords]);
const filtered = useMemo(() => {
return records.filter((r) => {
if (!inRange(String(r.createdAt), range)) return false;
@@ -55,7 +62,7 @@ export default function RecordsPage() {
}, [filtered]);
return (
<div className="shop-records-page">
<PullToRefresh onRefresh={loadRecords} className="shop-records-page">
<header className="shop-records-header">
<h1 className="app-page-title"></h1>
</header>
@@ -176,6 +183,6 @@ export default function RecordsPage() {
</div>
)}
</div>
</div>
</PullToRefresh>
);
}
+12 -7
View File
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
import { useStoreSession } from '../contexts/StoreSessionContext';
import {
needsStoreSelection,
@@ -19,15 +20,19 @@ export default function SelectStorePage() {
const currentStoreId = store?.storeId || '';
const canGoBack = Boolean(currentStoreId);
const loadStores = useCallback(() => {
return request<ShopStoreOption[]>('SHOP_H5', '/shop/auth/stores')
.then((list) => setStores(list))
.catch((e) => setMsg(e instanceof Error ? e.message : '加载门店失败'));
}, []);
useEffect(() => {
if (!authenticated) {
navigate('/login', { replace: true });
return;
}
void request<ShopStoreOption[]>('SHOP_H5', '/shop/auth/stores')
.then((list) => setStores(list))
.catch((e) => setMsg(e instanceof Error ? e.message : '加载门店失败'));
}, [authenticated, navigate]);
void loadStores();
}, [authenticated, navigate, loadStores]);
async function onSelect(storeId: string) {
if (loadingId) return;
@@ -63,7 +68,7 @@ export default function SelectStorePage() {
}
return (
<div className="shop-select-store-page">
<PullToRefresh onRefresh={loadStores} className="shop-select-store-page">
<header className="shop-subpage-header">
{canGoBack ? (
<button
@@ -151,7 +156,7 @@ export default function SelectStorePage() {
) : null}
</section>
</div>
</div>
</PullToRefresh>
);
}
+3 -2
View File
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { STORE_STAFF_ROLE_LABELS, type StoreStaffRole } from '@dukang/shared-types';
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
import { useStoreSession } from '../contexts/StoreSessionContext';
import { getStoreProfile, request } from '../lib/api';
@@ -116,7 +117,7 @@ export default function StaffPage() {
form.storeIds.length === 0 ? ownedStores.length : form.storeIds.length;
return (
<div className="shop-staff-page">
<PullToRefresh onRefresh={reload} className="shop-staff-page">
<header className="shop-subpage-header">
<button
type="button"
@@ -322,6 +323,6 @@ export default function StaffPage() {
</section>
)}
</div>
</div>
</PullToRefresh>
);
}