Files
xinlong-shop-taro/src/pages/user/vip-upgrade/index.tsx
赵忠林 994695c405 feat(store): 新增门店中心及VIP会员升级功能
- 个人信息页新增门店名称和门店地址展示,支持异步加载与只读显示
- 用户信息卡片整体改为绿色渐变背景,添加光晕和白色边框样式
- 新增升级VIP会员入口,突出展示并添加推荐标签
- 新建VIP会员升级页面,包含门店信息表单和VIP权益预览
- 提交升级申请调用新增api,支持状态检测表单只读
- 门店中心页面重构,简化订单管理,新增功能卡片及VIP审核入口
- 页面加载校验店员身份,非店员拒绝访问并提示
- 购物相关页面和组件全面支持VIP会员价格优先显示dealerPrice
- 新增ShopDealerApply模型门店相关字段,丰富会员申请数据记录
2026-07-04 10:26:47 +08:00

209 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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