fix(api): 替换所有接口请求的域名为 guilixu-api.websoft.top
- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api - 更新图片上传接口地址为新的 guilixu-api 域名 - 修改用户推广页面中邀请码链接和二维码接口的域名 - 更改注册页微信登录接口请求的域名为 guilixu-api
This commit is contained in:
231
src/pages/shop/cart.tsx
Normal file
231
src/pages/shop/cart.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useCartContext } from '@/contexts/CartContext'
|
||||
import { useUserContext } from '@/contexts/UserContext'
|
||||
import { pageShopGoods } from '@/api/shop/shopGoods'
|
||||
import type { ShopGoods } from '@/api/shop/shopGoods/model'
|
||||
import EmptyState from '@/components/common/EmptyState'
|
||||
import Loading from '@/components/common/Loading'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '购物车',
|
||||
})
|
||||
|
||||
const CartPage: React.FC = () => {
|
||||
const { items, selectedCount, selectedPrice, updateQuantity, toggleSelect, selectAll, removeItem, refresh, loading, removeSelected, addItem } = useCartContext()
|
||||
const { isLoggedIn } = useUserContext()
|
||||
const [recommendPage, setRecommendPage] = useState(1)
|
||||
const [refreshingRecommend, setRefreshingRecommend] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoggedIn) {
|
||||
refresh()
|
||||
fetchRecommendGoods(1)
|
||||
}
|
||||
}, [isLoggedIn])
|
||||
|
||||
const fetchRecommendGoods = async (page?: number) => {
|
||||
const targetPage = page || recommendPage
|
||||
try {
|
||||
setRefreshingRecommend(true)
|
||||
const res = await pageShopGoods({ page: targetPage, limit: 6 })
|
||||
if (res && res.list && res.list.length > 0) {
|
||||
setRecommendGoods(res.list || [])
|
||||
setRecommendPage(targetPage + 1)
|
||||
} else {
|
||||
// 没有更多数据时回到第一页重新请求
|
||||
if (targetPage > 1) {
|
||||
const firstRes = await pageShopGoods({ page: 1, limit: 6 })
|
||||
setRecommendGoods(firstRes?.list || [])
|
||||
setRecommendPage(2)
|
||||
} else {
|
||||
setRecommendGoods([])
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取推荐商品失败', err)
|
||||
} finally {
|
||||
setRefreshingRecommend(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 换一批
|
||||
const handleRefreshRecommend = () => {
|
||||
if (!refreshingRecommend) {
|
||||
fetchRecommendGoods()
|
||||
}
|
||||
}
|
||||
|
||||
const handleCheckout = () => {
|
||||
if (selectedCount === 0) {
|
||||
Taro.showToast({ title: '请选择商品', icon: 'none' })
|
||||
return
|
||||
}
|
||||
Taro.navigateTo({ url: '/pages/shop/checkout' })
|
||||
}
|
||||
|
||||
// 未登录状态
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<View className="min-h-screen bg-gray-50 flex flex-col">
|
||||
<View className="flex-1 flex items-center justify-center">
|
||||
<EmptyState
|
||||
text="请先登录"
|
||||
actionText="去登录"
|
||||
onAction={() => Taro.navigateTo({ url: '/pages/passport/login' })}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="flex flex-col min-h-screen">
|
||||
{/* 主要内容区域 - 使用ScrollView实现滚动 */}
|
||||
<ScrollView scrollY className="flex-1 bg-gray-50">
|
||||
{items.length === 0 && !loading ? (
|
||||
<View className="flex items-center justify-center py-20">
|
||||
<EmptyState text="购物车是空的" actionText="去逛逛" onAction={() => Taro.switchTab({ url: '/pages/index/index' })} />
|
||||
</View>
|
||||
) : (
|
||||
<View className="p-3">
|
||||
{/* 全选 */}
|
||||
<View className="flex items-center justify-between mb-3">
|
||||
<View className="flex items-center gap-2">
|
||||
<View
|
||||
className="w-5 h-5 rounded-full border border-gray-300 flex items-center justify-center"
|
||||
onClick={() => selectAll(!items.every(i => i.checked))}
|
||||
style={{ backgroundColor: items.length > 0 && items.every(i => i.checked) ? '#0e932e' : 'transparent' }}
|
||||
>
|
||||
{items.length > 0 && items.every(i => i.checked) && <Text className="text-white text-xs">✓</Text>}
|
||||
</View>
|
||||
<Text className="text-sm text-gray-600">全选</Text>
|
||||
</View>
|
||||
{selectedCount > 0 && (
|
||||
<View onClick={removeSelected}>
|
||||
<Text className="text-xs text-gray-400">删除选中</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 商品列表 */}
|
||||
{loading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
items.map((item) => (
|
||||
<View key={`${item.goodsId}-${item.skuId}`} className="bg-white rounded-lg p-3 mb-2 flex gap-3">
|
||||
<View
|
||||
className="w-5 h-5 rounded-full border border-gray-300 flex items-center justify-center mt-2 flex-shrink-0"
|
||||
onClick={() => toggleSelect(item.goodsId, item.skuId)}
|
||||
style={{ backgroundColor: item.checked ? '#0e932e' : 'transparent' }}
|
||||
>
|
||||
{item.checked && <Text className="text-white text-xs">✓</Text>}
|
||||
</View>
|
||||
<Image
|
||||
className="w-20 h-20 rounded-md bg-gray-100 flex-shrink-0"
|
||||
src={item.goodsImage || item.product?.image || ''}
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<View className="flex-1 flex flex-col justify-between">
|
||||
<Text className="text-sm text-gray-700 flex-1 line-clamp-2">
|
||||
{item.goodsName || item.product?.name || item.product?.goodsName || '商品名称'}
|
||||
</Text>
|
||||
{item.skuSpec && (
|
||||
<Text className="text-xs text-gray-400 mb-1">{item.skuSpec}</Text>
|
||||
)}
|
||||
<View className="flex justify-between items-center">
|
||||
<Text className="text-sm font-bold text-red-500">
|
||||
¥{item.skuPrice || item.sku?.price || item.product?.salePrice || item.product?.price || '0'}
|
||||
</Text>
|
||||
<View className="flex items-center gap-3">
|
||||
<View className="w-6 h-6 rounded bg-gray-100 flex items-center justify-center" onClick={() => updateQuantity(item.goodsId, item.skuId, item.quantity - 1)}>
|
||||
<Text className="text-gray-500 text-sm">-</Text>
|
||||
</View>
|
||||
<Text className="text-sm w-6 text-center">{item.quantity}</Text>
|
||||
<View className="w-6 h-6 rounded bg-gray-100 flex items-center justify-center" onClick={() => updateQuantity(item.goodsId, item.skuId, item.quantity + 1)}>
|
||||
<Text className="text-gray-500 text-sm">+</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center ml-1 flex-shrink-0" onClick={() => removeItem(item.goodsId, item.skuId)}>
|
||||
<Text className="text-gray-300 text-sm">✕</Text>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
|
||||
{/* 推荐商品 */}
|
||||
{recommendGoods.length > 0 && (
|
||||
<View className="mt-3">
|
||||
<View className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm font-medium text-gray-700">猜你喜欢</Text>
|
||||
<Text className="text-xs text-gray-400" onClick={handleRefreshRecommend}>
|
||||
{refreshingRecommend ? '加载中...' : '换一批'}
|
||||
</Text>
|
||||
</View>
|
||||
<ScrollView scrollX className="whitespace-nowrap" style={{ width: '100%', height: '180px' }}>
|
||||
<View className="flex gap-2" style={{ display: 'inline-flex' }}>
|
||||
{recommendGoods.map(goods => (
|
||||
<View
|
||||
key={goods.id}
|
||||
className="bg-white rounded-lg p-2 inline-block"
|
||||
style={{ width: '110px', flexShrink: 0 }}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/shop/product-detail?id=${goods.goodsId}` })}
|
||||
>
|
||||
<Image
|
||||
className="w-full rounded-md bg-gray-100"
|
||||
src={goods.image || ''}
|
||||
mode="aspectFill"
|
||||
style={{ height: '90px' }}
|
||||
/>
|
||||
<Text className="text-xs text-gray-700 mt-1 block" style={{ width: '100px' }} ellipsizeMode="tail" numberOfLines={1}>
|
||||
{goods.name || goods.title || '商品名称'}
|
||||
</Text>
|
||||
<View className="flex items-center justify-between mt-1">
|
||||
<Text className="text-xs font-bold text-red-500">
|
||||
¥{goods.salePrice || goods.price || '0'}
|
||||
</Text>
|
||||
<View
|
||||
className="w-5 h-5 rounded-full flex items-center justify-center"
|
||||
style={{ backgroundColor: '#0e932e' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
addItem(goods, undefined, 1)
|
||||
}}
|
||||
>
|
||||
<Text className="text-white text-xs">+</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部结算栏 */}
|
||||
{items.length > 0 && (
|
||||
<View className="flex items-center justify-between bg-white border-t border-gray-100 px-3 py-3">
|
||||
<View className="flex items-center gap-2">
|
||||
<Text className="text-sm text-gray-500">合计:</Text>
|
||||
<Text className="text-lg font-bold text-red-500">¥{selectedPrice}</Text>
|
||||
</View>
|
||||
<View
|
||||
className="px-6 py-2 rounded-full text-white text-sm font-medium"
|
||||
style={{ backgroundColor: selectedCount > 0 ? '#0e932e' : '#ccc' }}
|
||||
onClick={handleCheckout}
|
||||
>
|
||||
<Text>结算({selectedCount})</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default CartPage
|
||||
Reference in New Issue
Block a user