feat(store): 新增门店中心及VIP会员升级功能

- 个人信息页新增门店名称和门店地址展示,支持异步加载与只读显示
- 用户信息卡片整体改为绿色渐变背景,添加光晕和白色边框样式
- 新增升级VIP会员入口,突出展示并添加推荐标签
- 新建VIP会员升级页面,包含门店信息表单和VIP权益预览
- 提交升级申请调用新增api,支持状态检测表单只读
- 门店中心页面重构,简化订单管理,新增功能卡片及VIP审核入口
- 页面加载校验店员身份,非店员拒绝访问并提示
- 购物相关页面和组件全面支持VIP会员价格优先显示dealerPrice
- 新增ShopDealerApply模型门店相关字段,丰富会员申请数据记录
This commit is contained in:
2026-07-04 10:26:47 +08:00
parent 2deaaffe13
commit 994695c405
19 changed files with 1744 additions and 632 deletions

View File

@@ -0,0 +1,208 @@
import React, { useState, useEffect } from 'react'
import { View, Text, Input } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useUser } from '@/hooks/useUser'
import { addShopDealerApply, listShopDealerApply } from '@/api/shop/shopDealerApply'
import type { ShopDealerApply } from '@/api/shop/shopDealerApply/model'
definePageConfig({
navigationBarTitleText: '升级VIP会员',
})
const VipUpgradePage: React.FC = () => {
const { user, isLoggedIn } = useUser()
const [realName, setRealName] = useState('')
const [address, setAddress] = useState('')
const [loading, setLoading] = useState(false)
const [existingApply, setExistingApply] = useState<ShopDealerApply | null>(null)
useEffect(() => {
if (!isLoggedIn) {
Taro.navigateTo({ url: '/passport/login' })
return
}
if (user) {
// 门店名称和地址统一从 shop-dealer-apply 表读取
listShopDealerApply({ userId: (user as any)?.userId || (user as any)?.id })
.then(data => {
if (!data || data.length === 0) return
// 取最新一条申请记录(按 applyId 倒序)
const latest = [...data].sort((a, b) => (b.applyId || 0) - (a.applyId || 0))[0]
if (latest) {
setExistingApply(latest)
setRealName(latest.realName || '')
setAddress(latest.address || '')
}
})
.catch(() => {})
}
}, [user, isLoggedIn])
const handleSave = async () => {
if (!realName.trim()) {
Taro.showToast({ title: '请输入门店名称', icon: 'none' })
return
}
if (!address.trim()) {
Taro.showToast({ title: '请输入门店地址', icon: 'none' })
return
}
setLoading(true)
try {
await addShopDealerApply({
userId: (user as any)?.userId || (user as any)?.id,
realName: realName.trim(),
address: address.trim(),
applyType: 10, // 需后台审核
applyStatus: 10, // 待审核
} as ShopDealerApply)
Taro.showToast({ title: '提交成功,等待审核', icon: 'none' })
setTimeout(() => {
Taro.navigateBack()
}, 1500)
} catch (error) {
console.error('提交失败:', error)
Taro.showToast({ title: '提交失败,请重试', icon: 'none' })
} finally {
setLoading(false)
}
}
// 待审核(10) 和 已通过(20) 时只读展示;已驳回(30) 允许编辑后重新提交
const isReadonly = existingApply?.applyStatus === 10 || existingApply?.applyStatus === 20
if (!isLoggedIn) return null
return (
<View className='min-h-screen bg-gray-50'>
{/* 顶部渐变背景 */}
<View className='pt-8 pb-12 px-5 rounded-b-3xl relative overflow-hidden'
style={{ background: 'linear-gradient(135deg, #15803d 0%, #22c55e 60%, #4ade80 100%)' }}
>
<View className='absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10' style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
<View className='absolute -bottom-6 -left-6 w-24 h-24 rounded-full opacity-15' style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
<View className='relative z-10 flex flex-col items-center'>
<View className='w-16 h-16 rounded-full bg-white bg-opacity-20 flex items-center justify-center mb-3'>
<Text className='text-3xl'>👑</Text>
</View>
<Text className='text-white text-xl font-bold'>VIP会员</Text>
<Text className='text-white text-opacity-80 text-sm mt-1'>
{existingApply?.applyStatus === 20 ? '恭喜您已是VIP会员' : '填写门店信息申请VIP会员资格'}
</Text>
</View>
</View>
{/* 状态提示 */}
{existingApply && (
<View className='mx-3 -mt-6 bg-white rounded-xl p-4 shadow-sm relative z-20 mb-3'>
<View className='flex items-start gap-3'>
<View className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
existingApply.applyStatus === 10 ? 'bg-amber-100' : 'bg-green-100'
}`}>
<Text className='text-sm'>{existingApply.applyStatus === 10 ? '⏳' : '✅'}</Text>
</View>
<View className='flex-1'>
<Text className='text-gray-800 text-sm font-medium block'>
{existingApply.applyStatus === 10 ? '审核中' : '审核通过'}
</Text>
<Text className='text-gray-500 text-xs mt-1 block leading-relaxed'>
{existingApply.applyStatus === 10
? '您的VIP申请正在审核中请耐心等待。审核结果将通过消息通知您。'
: '恭喜您的VIP会员申请已审核通过现在可以享受全部VIP专属权益。'}
</Text>
</View>
</View>
</View>
)}
{/* 说明卡片 */}
{!existingApply && (
<View className='mx-3 -mt-6 bg-white rounded-xl p-4 shadow-sm relative z-20'>
<View className='flex items-start gap-3'>
<View className='w-8 h-8 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0'>
<Text className='text-sm'>💡</Text>
</View>
<View className='flex-1'>
<Text className='text-gray-800 text-sm font-medium block'></Text>
<Text className='text-gray-500 text-xs mt-1 block leading-relaxed'>
VIP会员专属权益
</Text>
</View>
</View>
</View>
)}
{/* 表单 */}
<View className={`${existingApply ? 'mt-3' : ''} mx-3 mt-3 bg-white rounded-xl overflow-hidden`}>
{/* 门店名称 */}
<View className='px-4 py-4 border-b border-gray-50'>
<Text className='text-sm text-gray-700 font-medium block mb-2'> <Text className='text-red-500'>*</Text></Text>
<Input
className='w-full text-sm text-gray-800 bg-gray-50 rounded-lg px-3 py-3'
placeholder='请输入您的门店名称'
value={realName}
onInput={(e: any) => setRealName(e.detail.value)}
maxlength={50}
disabled={isReadonly}
/>
</View>
{/* 门店地址 */}
<View className='px-4 py-4'>
<Text className='text-sm text-gray-700 font-medium block mb-2'> <Text className='text-red-500'>*</Text></Text>
<Input
className='w-full text-sm text-gray-800 bg-gray-50 rounded-lg px-3 py-3'
placeholder='请输入您的门店详细地址'
value={address}
onInput={(e: any) => setAddress(e.detail.value)}
maxlength={100}
disabled={isReadonly}
/>
</View>
</View>
{/* VIP权益预览 */}
<View className='mx-3 mt-4'>
<Text className='text-base font-medium text-gray-800 mb-3 block'>VIP会员专属权益</Text>
<View className='grid grid-cols-2 gap-3'>
{[
{ icon: '💎', title: '更高佣金', desc: '享受高额团队佣金' },
{ icon: '🎁', title: '专属折扣', desc: '购物享VIP专属价' },
{ icon: '⚡', title: '优先发货', desc: '订单优先处理配送' },
{ icon: '🎧', title: '专属客服', desc: '一对一VIP服务' },
].map(item => (
<View key={item.title} className='bg-white rounded-xl p-4 flex items-center gap-3'>
<View className='w-10 h-10 rounded-full bg-green-50 flex items-center justify-center flex-shrink-0'>
<Text className='text-lg'>{item.icon}</Text>
</View>
<View className='flex-1 min-w-0'>
<Text className='text-gray-800 text-sm font-medium block'>{item.title}</Text>
<Text className='text-gray-400 text-xs mt-0.5 block truncate'>{item.desc}</Text>
</View>
</View>
))}
</View>
</View>
{/* 提交按钮 */}
{!existingApply && (
<View className='mx-3 mt-6 mb-4'>
<View
className='w-full py-4 rounded-xl flex items-center justify-center active:opacity-90'
style={{ background: loading ? '#a3e635' : 'linear-gradient(135deg, #16a34a, #22c55e)' }}
onClick={loading ? undefined : handleSave}
>
<Text className='text-white text-base font-semibold'>
{loading ? '提交中...' : '提交申请'}
</Text>
</View>
<Text className='text-gray-400 text-xs mt-2 text-center block'>
1-3
</Text>
</View>
)}
</View>
)
}
export default VipUpgradePage