feat: audit diff, mock SMS 999888, env photos, proxy pay lock, mini-user UX

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 20:47:28 +08:00
parent d90847bad0
commit e93e4b8f84
23 changed files with 524 additions and 135 deletions
+20 -12
View File
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { View, Text, Image, Swiper, SwiperItem } from '@tarojs/components';
import Taro, {
useDidShow,
@@ -200,6 +200,18 @@ export default function HomePage() {
return map;
}, [products]);
const visibleAromaTabs = useMemo(
() => AROMA_TABS.filter((t) => productsByAroma[t.key].length > 0),
[productsByAroma],
);
useEffect(() => {
if (loading || visibleAromaTabs.length === 0) return;
if (!visibleAromaTabs.some((t) => t.key === activeAroma)) {
setActiveAroma(visibleAromaTabs[0].key);
}
}, [loading, visibleAromaTabs, activeAroma]);
const banners = miniHome.banners;
const footerUrl = miniHome.footerUrl;
@@ -246,18 +258,18 @@ export default function HomePage() {
if (now - lastScrollSyncAtRef.current < 80) return;
lastScrollSyncAtRef.current = now;
const query = Taro.createSelectorQuery();
AROMA_TABS.forEach((t) => {
visibleAromaTabs.forEach((t) => {
query.select(`#${aromaSectionId(t.key)}`).boundingClientRect();
});
query.exec((rects) => {
if (!Array.isArray(rects) || rects.length === 0) return;
let next: AromaKey = AROMA_TABS[0].key;
for (let i = 0; i < AROMA_TABS.length; i++) {
let next: AromaKey = visibleAromaTabs[0]?.key ?? AROMA_TABS[0].key;
for (let i = 0; i < visibleAromaTabs.length; i++) {
const rect = rects[i] as { top?: number } | null;
if (!rect || rect.top == null) continue;
// 区块顶进入导航下方一带时视为当前香型
if (rect.top <= AROMA_NAV_OFFSET_PX + 24) {
next = AROMA_TABS[i].key;
next = visibleAromaTabs[i].key;
}
}
setActiveAroma((prev) => (prev === next ? prev : next));
@@ -341,7 +353,7 @@ export default function HomePage() {
<View className="home-aroma-nav">
<View className="home-aroma-tabs">
{AROMA_TABS.map((t) => (
{visibleAromaTabs.map((t) => (
<Text
key={t.key}
className={`home-aroma-tab${activeAroma === t.key ? ' home-aroma-tab--active' : ''}`}
@@ -361,16 +373,12 @@ export default function HomePage() {
) : null}
{!loading &&
products.length > 0 &&
AROMA_TABS.map((t) => {
visibleAromaTabs.map((t) => {
const list = productsByAroma[t.key];
return (
<View key={t.key} id={aromaSectionId(t.key)} className="home-aroma-section">
<Text className="home-aroma-section-title">{t.label}</Text>
{list.length === 0 ? (
<View className="home-empty home-empty--section">线</View>
) : (
list.map((p) => renderProductCard(p))
)}
{list.map((p) => renderProductCard(p))}
</View>
);
})}
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { View, Text, Image, Textarea } from '@tarojs/components';
import { View, Text, Image } from '@tarojs/components';
import Taro, {
useDidShow,
useLoad,
@@ -152,9 +152,6 @@ export default function StoreDetailPage() {
const [recentRedeems, setRecentRedeems] = useState<RecentRedeem[]>([]);
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState('');
const [disputeOpen, setDisputeOpen] = useState(false);
const [disputeRemark, setDisputeRemark] = useState('');
const [disputeSubmitting, setDisputeSubmitting] = useState(false);
const [headerSolid, setHeaderSolid] = useState(false);
const storeRef = useRef<Store | null>(null);
storeRef.current = store;
@@ -276,27 +273,6 @@ export default function StoreDetailPage() {
Taro.makePhoneCall({ phoneNumber: store.phone }).catch(() => toast('无法拨打电话'));
}
async function submitPackageDispute() {
if (!storeId) return;
setDisputeSubmitting(true);
try {
await request('/trade/package-disputes', {
method: 'POST',
body: JSON.stringify({
storeId,
remark: disputeRemark.trim() || '用户对门店套餐有异议',
}),
});
toast('已提交套餐异议');
setDisputeOpen(false);
setDisputeRemark('');
} catch (e) {
toast(e instanceof Error ? e.message : '提交失败');
} finally {
setDisputeSubmitting(false);
}
}
function openMap() {
if (!store) return;
const lat = store.latitude != null ? Number(store.latitude) : NaN;
@@ -440,9 +416,6 @@ export default function StoreDetailPage() {
) : null}
</View>
))}
<Text className="store-detail-package-dispute" onClick={() => setDisputeOpen(true)}>
</Text>
</View>
) : null}
@@ -477,33 +450,6 @@ export default function StoreDetailPage() {
</View>
) : null}
{disputeOpen ? (
<View className="store-detail-dispute-mask" onClick={() => !disputeSubmitting && setDisputeOpen(false)}>
<View className="store-detail-dispute-panel" onClick={(e) => e.stopPropagation()}>
<Text className="store-detail-section-title"></Text>
<Text className="store-detail-package-meta"></Text>
<Textarea
className="store-detail-dispute-input"
value={disputeRemark}
maxlength={500}
placeholder="请填写异议说明(选填)"
onInput={(e) => setDisputeRemark(e.detail.value)}
/>
<View className="store-detail-dispute-actions">
<View className="u-btn u-btn--ghost" onClick={() => setDisputeOpen(false)}>
<Text></Text>
</View>
<View
className="u-btn"
onClick={() => !disputeSubmitting && void submitPackageDispute()}
>
<Text>{disputeSubmitting ? '提交中…' : '提交'}</Text>
</View>
</View>
</View>
</View>
) : null}
<View className="store-detail-bar">
<View
className="u-btn u-btn--block"