feat(rider): 新增水票核销功能
- 添加水票核销扫码页面,支持扫描加密和明文二维码 - 实现水票验证逻辑,包括余额检查和核销确认 - 添加核销记录展示,最多保留最近10条记录 - 在骑手端界面增加水票核销入口 - 新增获取用户水票总数的API接口 - 优化首页轮播图加载,增加缓存和懒加载机制 - 添加门店选择功能,支持订单确认页切换门店 - 修复物流信息类型安全问题 - 更新用户中心门店相关文案显示
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
InputNumber,
|
||||
ConfigProvider
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Location, ArrowRight} from '@nutui/icons-react-taro'
|
||||
import {Location, ArrowRight, Shop} from '@nutui/icons-react-taro'
|
||||
import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
||||
@@ -37,6 +37,9 @@ import {
|
||||
filterUnusableCoupons
|
||||
} from "@/utils/couponUtils";
|
||||
import navTo from "@/utils/common";
|
||||
import type {ShopStore} from "@/api/shop/shopStore/model";
|
||||
import {getShopStore, listShopStore} from "@/api/shop/shopStore";
|
||||
import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection";
|
||||
|
||||
|
||||
const OrderConfirm = () => {
|
||||
@@ -67,9 +70,37 @@ const OrderConfirm = () => {
|
||||
const [availableCoupons, setAvailableCoupons] = useState<CouponCardProps[]>([])
|
||||
const [couponLoading, setCouponLoading] = useState<boolean>(false)
|
||||
|
||||
// 门店选择:用于在下单页展示当前“已选门店”,并允许用户切换(写入 SelectedStore Storage)
|
||||
const [storePopupVisible, setStorePopupVisible] = useState(false)
|
||||
const [stores, setStores] = useState<ShopStore[]>([])
|
||||
const [storeLoading, setStoreLoading] = useState(false)
|
||||
const [selectedStore, setSelectedStore] = useState<ShopStore | null>(getSelectedStoreFromStorage())
|
||||
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const goodsId = router?.params?.goodsId;
|
||||
|
||||
const loadStores = async () => {
|
||||
if (storeLoading) return
|
||||
try {
|
||||
setStoreLoading(true)
|
||||
const list = await listShopStore()
|
||||
setStores((list || []).filter(s => s?.isDelete !== 1))
|
||||
} catch (e) {
|
||||
console.error('获取门店列表失败:', e)
|
||||
setStores([])
|
||||
Taro.showToast({title: '获取门店列表失败', icon: 'none'})
|
||||
} finally {
|
||||
setStoreLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const openStorePopup = async () => {
|
||||
setStorePopupVisible(true)
|
||||
if (!stores.length) {
|
||||
await loadStores()
|
||||
}
|
||||
}
|
||||
|
||||
// 计算商品总价
|
||||
const getGoodsTotal = () => {
|
||||
if (!goods) return 0
|
||||
@@ -561,6 +592,8 @@ const OrderConfirm = () => {
|
||||
}
|
||||
|
||||
useDidShow(() => {
|
||||
// 返回/切换到该页面时,刷新一下当前已选门店
|
||||
setSelectedStore(getSelectedStoreFromStorage())
|
||||
loadAllData()
|
||||
})
|
||||
|
||||
@@ -623,6 +656,26 @@ const OrderConfirm = () => {
|
||||
)}
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup>
|
||||
<Cell
|
||||
title={(
|
||||
<View className="flex items-center gap-2">
|
||||
<Shop className={'text-gray-500'}/>
|
||||
<Text>门店</Text>
|
||||
</View>
|
||||
)}
|
||||
extra={(
|
||||
<View className={'flex items-center gap-2'}>
|
||||
<View className={'text-gray-900'}>
|
||||
{selectedStore?.name || '请选择门店'}
|
||||
</View>
|
||||
<ArrowRight className={'text-gray-400'} size={14}/>
|
||||
</View>
|
||||
)}
|
||||
onClick={openStorePopup}
|
||||
/>
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup>
|
||||
<Cell key={goods.goodsId}>
|
||||
<View className={'flex w-full justify-between gap-3'}>
|
||||
@@ -816,6 +869,63 @@ const OrderConfirm = () => {
|
||||
</View>
|
||||
</Popup>
|
||||
|
||||
{/* 门店选择弹窗 */}
|
||||
<Popup
|
||||
visible={storePopupVisible}
|
||||
position="bottom"
|
||||
style={{height: '70vh'}}
|
||||
onClose={() => setStorePopupVisible(false)}
|
||||
>
|
||||
<View className="p-4">
|
||||
<View className="flex justify-between items-center mb-3">
|
||||
<Text className="text-base font-medium">选择门店</Text>
|
||||
<Text
|
||||
className="text-sm text-gray-500"
|
||||
onClick={() => setStorePopupVisible(false)}
|
||||
>
|
||||
关闭
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{storeLoading ? (
|
||||
<View className="py-10 text-center text-gray-500">
|
||||
<Text>加载中...</Text>
|
||||
</View>
|
||||
) : (
|
||||
<CellGroup>
|
||||
{stores.map((s) => {
|
||||
const isActive = !!selectedStore?.id && selectedStore.id === s.id
|
||||
return (
|
||||
<Cell
|
||||
key={s.id}
|
||||
title={<Text className={isActive ? 'text-green-600' : ''}>{s.name || `门店${s.id}`}</Text>}
|
||||
description={s.address || ''}
|
||||
onClick={async () => {
|
||||
let storeToSave: ShopStore = s
|
||||
if (s?.id) {
|
||||
try {
|
||||
const full = await getShopStore(s.id)
|
||||
if (full) storeToSave = full
|
||||
} catch (_e) {
|
||||
// keep base item
|
||||
}
|
||||
}
|
||||
setSelectedStore(storeToSave)
|
||||
saveSelectedStoreToStorage(storeToSave)
|
||||
setStorePopupVisible(false)
|
||||
Taro.showToast({title: '门店已切换', icon: 'success'})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{!stores.length && (
|
||||
<Cell title={<Text className="text-gray-500">暂无门店数据</Text>} />
|
||||
)}
|
||||
</CellGroup>
|
||||
)}
|
||||
</View>
|
||||
</Popup>
|
||||
|
||||
<Gap height={50}/>
|
||||
|
||||
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10 border-t border-gray-200'}>
|
||||
|
||||
Reference in New Issue
Block a user