feat(rider): 新增水票核销功能
- 添加水票核销扫码页面,支持扫描加密和明文二维码 - 实现水票验证逻辑,包括余额检查和核销确认 - 添加核销记录展示,最多保留最近10条记录 - 在骑手端界面增加水票核销入口 - 新增获取用户水票总数的API接口 - 优化首页轮播图加载,增加缓存和懒加载机制 - 添加门店选择功能,支持订单确认页切换门店 - 修复物流信息类型安全问题 - 更新用户中心门店相关文案显示
This commit is contained in:
@@ -6,6 +6,7 @@ import {Image} from '@nutui/nutui-react-taro'
|
||||
import {getCmsAdByCode} from "@/api/cms/cmsAd";
|
||||
import navTo from "@/utils/common";
|
||||
import {pageCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import Taro from '@tarojs/taro'
|
||||
|
||||
type AdImage = {
|
||||
url?: string
|
||||
@@ -41,41 +42,45 @@ const MyPage = () => {
|
||||
const [loading, setLoading] = useState(true)
|
||||
// const [disableSwiper, setDisableSwiper] = useState(false)
|
||||
|
||||
const CACHE_KEY = 'home_banner_mp-ad'
|
||||
|
||||
// 用于记录触摸开始位置
|
||||
// const touchStartRef = useRef({x: 0, y: 0})
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
setLoading(true)
|
||||
const loadData = async (opts?: {silent?: boolean}) => {
|
||||
if (!opts?.silent) setLoading(true)
|
||||
try {
|
||||
const [flashRes] = await Promise.allSettled([
|
||||
getCmsAdByCode('mp-ad'),
|
||||
getCmsAdByCode('hot_today'),
|
||||
pageCmsArticle({ limit: 1, recommend: 1 }),
|
||||
])
|
||||
|
||||
if (flashRes.status === 'fulfilled') {
|
||||
console.log('flashflashflash', flashRes.value)
|
||||
setCarouselData(flashRes.value)
|
||||
} else {
|
||||
console.error('Failed to fetch flash:', flashRes.reason)
|
||||
}
|
||||
|
||||
// 只阻塞 banner 自己的数据;其他数据预热不应影响首屏展示速度
|
||||
const flash = await getCmsAdByCode('mp-ad')
|
||||
setCarouselData(flash)
|
||||
void Taro.setStorage({ key: CACHE_KEY, data: flash }).catch(() => {})
|
||||
} catch (error) {
|
||||
console.error('Banner数据加载失败:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
if (!opts?.silent) setLoading(false)
|
||||
}
|
||||
|
||||
// 后台预热(不阻塞 banner 渲染)
|
||||
void getCmsAdByCode('hot_today').catch(() => {})
|
||||
void pageCmsArticle({ limit: 1, recommend: 1 }).catch(() => {})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadData().then()
|
||||
const cached = Taro.getStorageSync(CACHE_KEY) as CmsAd | undefined
|
||||
// 有缓存则先渲染缓存,避免首屏等待;再静默刷新
|
||||
if (cached && normalizeAdImages(cached).length) {
|
||||
setCarouselData(cached)
|
||||
setLoading(false)
|
||||
void loadData({ silent: true })
|
||||
return
|
||||
}
|
||||
void loadData()
|
||||
}, [])
|
||||
|
||||
// 轮播图高度,默认300px
|
||||
const carouselHeight = toNumberPx(carouselData?.height, 300)
|
||||
const carouselImages = normalizeAdImages(carouselData)
|
||||
console.log(carouselImages,'carouselImages')
|
||||
|
||||
// 骨架屏组件
|
||||
const BannerSkeleton = () => (
|
||||
@@ -149,7 +154,8 @@ const MyPage = () => {
|
||||
src={src}
|
||||
mode={'scaleToFill'}
|
||||
onClick={() => (img.path ? navTo(`${img.path}`) : undefined)}
|
||||
lazyLoad={false}
|
||||
// 首张图优先加载,其余按需懒加载,避免并发图片请求拖慢首屏可见
|
||||
lazyLoad={index !== 0}
|
||||
style={{
|
||||
height: `${carouselHeight}px`,
|
||||
borderRadius: '4px',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Header from './Header'
|
||||
import Banner from './Banner'
|
||||
import Taro, { useShareAppMessage } from '@tarojs/taro'
|
||||
import Taro, { useDidShow, useShareAppMessage } from '@tarojs/taro'
|
||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import { Cart, Coupon, Gift, Ticket } from '@nutui/icons-react-taro'
|
||||
@@ -8,11 +8,13 @@ import { getShopInfo } from '@/api/layout'
|
||||
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
||||
import { pageShopGoods } from '@/api/shop/shopGoods'
|
||||
import type { ShopGoods, ShopGoodsParam } from '@/api/shop/shopGoods/model'
|
||||
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
|
||||
import './index.scss'
|
||||
|
||||
function Home() {
|
||||
const [activeTabKey, setActiveTabKey] = useState('recommend')
|
||||
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
|
||||
const [ticketTotal, setTicketTotal] = useState(0)
|
||||
|
||||
useShareAppMessage(() => {
|
||||
// 获取当前用户ID,用于生成邀请链接
|
||||
@@ -87,9 +89,24 @@ function Home() {
|
||||
// const handleTabsStickyChange = (isSticky: boolean) => {}
|
||||
|
||||
const reload = () => {
|
||||
|
||||
const token = Taro.getStorageSync('access_token')
|
||||
if (!token) {
|
||||
setTicketTotal(0)
|
||||
return
|
||||
}
|
||||
getMyGltUserTicketTotal()
|
||||
.then((total) => setTicketTotal(typeof total === 'number' ? total : 0))
|
||||
.catch((err) => {
|
||||
console.error('首页水票总数加载失败:', err)
|
||||
setTicketTotal(0)
|
||||
})
|
||||
};
|
||||
|
||||
// 回到首页/首次进入时都刷新一次(避免依赖 scope.userInfo 导致不触发 reload)
|
||||
useDidShow(() => {
|
||||
reload()
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// 获取站点信息
|
||||
getShopInfo().then(() => {
|
||||
@@ -132,7 +149,6 @@ function Home() {
|
||||
if (res.authSetting['scope.userInfo']) {
|
||||
// 用户已经授权过,可以直接获取用户信息
|
||||
console.log('用户已经授权过,可以直接获取用户信息')
|
||||
reload();
|
||||
} else {
|
||||
// 用户未授权,需要弹出授权窗口
|
||||
console.log('用户未授权,需要弹出授权窗口')
|
||||
@@ -227,7 +243,7 @@ function Home() {
|
||||
<View className="ticket-card__head">
|
||||
<Text className="ticket-card__title">电子水票</Text>
|
||||
<Text className="ticket-card__count">
|
||||
您还有 <Text className="ticket-card__countNum">0</Text> 张水票
|
||||
您还有 <Text className="ticket-card__countNum">{ticketTotal}</Text> 张水票
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ const IsDealer = () => {
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Reward className={'text-orange-100 '} size={16}/>
|
||||
<Text style={{fontSize: '16px'}}
|
||||
className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店入驻'}</Text>
|
||||
className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店中心'}</Text>
|
||||
{/*<Text className={'text-white opacity-80 pl-3'}>门店核销</Text>*/}
|
||||
</View>
|
||||
}
|
||||
@@ -75,7 +75,7 @@ const IsDealer = () => {
|
||||
title={
|
||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||
<Reward className={'text-orange-100 '} size={16}/>
|
||||
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店入驻'}</Text>
|
||||
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店中心'}</Text>
|
||||
<Text className={'text-white opacity-80 pl-3'}>{config?.vipComments || ''}</Text>
|
||||
</View>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user