feat(store): 新增门店中心及VIP会员升级功能
- 个人信息页新增门店名称和门店地址展示,支持异步加载与只读显示 - 用户信息卡片整体改为绿色渐变背景,添加光晕和白色边框样式 - 新增升级VIP会员入口,突出展示并添加推荐标签 - 新建VIP会员升级页面,包含门店信息表单和VIP权益预览 - 提交升级申请调用新增api,支持状态检测表单只读 - 门店中心页面重构,简化订单管理,新增功能卡片及VIP审核入口 - 页面加载校验店员身份,非店员拒绝访问并提示 - 购物相关页面和组件全面支持VIP会员价格优先显示dealerPrice - 新增ShopDealerApply模型门店相关字段,丰富会员申请数据记录
This commit is contained in:
@@ -9,6 +9,7 @@ import EmptyState from '@/components/common/EmptyState'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { isGuest } from '@/utils/auth'
|
||||
import { requireLogin } from '@/utils/login-guard'
|
||||
import { isVipMember } from '@/utils/vip'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '购物车',
|
||||
@@ -180,7 +181,7 @@ const CartPage: React.FC = () => {
|
||||
)}
|
||||
<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'}
|
||||
¥{isVipMember() && (item.product as any)?.dealerPrice ? (item.product as any).dealerPrice : (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)}>
|
||||
@@ -229,7 +230,7 @@ const CartPage: React.FC = () => {
|
||||
</Text>
|
||||
<View className="flex items-center justify-between mt-1">
|
||||
<Text className="text-xs font-bold text-red-500">
|
||||
¥{goods.salePrice || goods.price || '0'}
|
||||
¥{isVipMember() && goods.dealerPrice ? goods.dealerPrice : (goods.salePrice || goods.price || '0')}
|
||||
</Text>
|
||||
<View
|
||||
className="w-5 h-5 rounded-full flex items-center justify-center"
|
||||
|
||||
@@ -13,6 +13,7 @@ import { listShopGoods } from '@/api/shop/shopGoods'
|
||||
import type { ShopGoods } from '@/api/shop/shopGoods/model'
|
||||
import type { ShopUserCoupon } from '@/api/shop/shopUserCoupon/model'
|
||||
import type { OrderGoodsItem, OrderCreateRequest } from '@/api/shop/shopOrder/model'
|
||||
import { isVipMember } from '@/utils/vip'
|
||||
|
||||
// 满减门槛配置
|
||||
const THRESHOLDS = [
|
||||
@@ -145,8 +146,12 @@ const CheckoutPage: React.FC = () => {
|
||||
// 计算金额
|
||||
const goodsPrice = useMemo(() => {
|
||||
if (!items || items.length === 0) return 0
|
||||
const vip = isVipMember()
|
||||
return items.reduce((sum, item) => {
|
||||
return sum + Number(item.skuPrice || item.sku?.price || item.product?.salePrice || item.product?.price || 0) * (item.quantity || item.num || 1)
|
||||
// VIP 会员优先使用 dealerPrice
|
||||
const dealerPrice = vip ? (item.product as any)?.dealerPrice : null
|
||||
const unitPrice = dealerPrice || item.skuPrice || item.sku?.price || item.product?.salePrice || item.product?.price || 0
|
||||
return sum + Number(unitPrice) * (item.quantity || item.num || 1)
|
||||
}, 0)
|
||||
}, [buyNowItems, selectedItems])
|
||||
|
||||
@@ -362,7 +367,7 @@ const CheckoutPage: React.FC = () => {
|
||||
<View className='flex justify-between items-center mt-1'>
|
||||
<Text className='text-xs text-gray-500'>x{item.quantity || item.num || 1}</Text>
|
||||
<Text className='text-sm font-medium text-gray-800'>
|
||||
¥{item.skuPrice || item.sku?.salePrice || item.sku?.price || item.product?.salePrice || item.product?.price || '0'}
|
||||
¥{isVipMember() && (item.product as any)?.dealerPrice ? (item.product as any).dealerPrice : (item.skuPrice || item.sku?.salePrice || item.sku?.price || item.product?.salePrice || item.product?.price || '0')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useUserContext } from '@/contexts/UserContext'
|
||||
import { useScrollHeight } from '@/hooks/useScrollHeight'
|
||||
import { isGuest } from '@/utils/auth'
|
||||
import { requireLogin } from '@/utils/login-guard'
|
||||
import { isVipMember } from '@/utils/vip'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '商品详情',
|
||||
@@ -66,6 +67,14 @@ const ProductDetailPage: React.FC = () => {
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// VIP 会员显示 dealerPrice,普通用户显示 price
|
||||
const getDisplayPrice = (): string => {
|
||||
if (isVipMember() && product?.dealerPrice) {
|
||||
return product.dealerPrice
|
||||
}
|
||||
return product?.price || '0'
|
||||
}
|
||||
|
||||
const checkFavoriteStatus = async () => {
|
||||
try {
|
||||
const status = await getShopGoodsFavoriteStatus({ goodsId: id })
|
||||
@@ -142,6 +151,8 @@ const ProductDetailPage: React.FC = () => {
|
||||
console.error('[ProductDetail] 加入购物车失败:', err)
|
||||
}
|
||||
} else {
|
||||
// VIP 会员使用 dealerPrice 作为结算价
|
||||
const vipPrice = isVipMember() && product.dealerPrice ? product.dealerPrice : undefined
|
||||
const buyNowData = [{
|
||||
goodsId: product.goodsId!,
|
||||
skuId: sku?.id || 0,
|
||||
@@ -149,6 +160,7 @@ const ProductDetailPage: React.FC = () => {
|
||||
num: quantity,
|
||||
product: product,
|
||||
sku: sku,
|
||||
skuPrice: vipPrice,
|
||||
checked: true,
|
||||
}]
|
||||
|
||||
@@ -261,14 +273,22 @@ const ProductDetailPage: React.FC = () => {
|
||||
{/* 价格区域 */}
|
||||
<View className='bg-white p-4'>
|
||||
<View className='flex items-baseline gap-2'>
|
||||
<Price price={product.price || '0'} size='large' color='#ee0a24' loginMask />
|
||||
<Price price={getDisplayPrice()} size='large' color='#ee0a24' loginMask />
|
||||
<Tag>到手价</Tag>
|
||||
{!isGuest() && product.salePrice && product.salePrice !== product.price && (
|
||||
<Text className='text-xs text-gray-400 ml-2'>¥{product.salePrice}</Text>
|
||||
{/* VIP 会员价 */}
|
||||
{!isGuest() && isVipMember() && Number(product.dealerPrice) > 0 ? (
|
||||
<View className='ml-auto inline-flex items-center gap-1 bg-amber-50 rounded px-2 py-1'>
|
||||
<Text className='text-xs text-amber-600 font-medium'>VIP专享</Text>
|
||||
<Text className='text-sm text-amber-700 font-bold'>¥{product.dealerPrice}</Text>
|
||||
</View>
|
||||
) : (
|
||||
!isGuest() && Number(product.salePrice) > 0 && product.salePrice !== product.price && (
|
||||
<Text className='text-xs text-gray-400 ml-2 line-through'>¥{product.salePrice}</Text>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
{/* 会员价 */}
|
||||
{!isGuest() && product.memberStorePrice && product.memberStorePrice !== product.price && (
|
||||
{!isGuest() && !isVipMember() && Number(product.memberStorePrice) > 0 && product.memberStorePrice !== product.price && (
|
||||
<View className='mt-2 inline-block bg-orange-50 rounded px-2 py-1'>
|
||||
<Text className='text-xs text-orange-500'>会员价: ¥{product.memberStorePrice}</Text>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user