Files
xinlong-shop-taro/src/pages/exchange.tsx
赵忠林 f3886664f7 fix(api): 替换所有接口请求的域名为 guilixu-api.websoft.top
- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api
- 更新图片上传接口地址为新的 guilixu-api 域名
- 修改用户推广页面中邀请码链接和二维码接口的域名
- 更改注册页微信登录接口请求的域名为 guilixu-api
2026-06-16 17:15:59 +08:00

227 lines
7.8 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 } from 'react'
import { View, Text, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useRequest } from '@/hooks/useRequest'
import { listShopGoods, type ShopGoods } from '@/api/shop/shopGoods'
definePageConfig({
navigationBarTitleText: '积分兑换',
})
// 兑换分类
const categories = [
{ label: '全部', value: 0 },
{ label: '优惠券', value: 1 },
{ label: '实物商品', value: 2 },
{ label: '虚拟商品', value: 3 },
]
const ExchangePage: React.FC = () => {
const [activeCat, setActiveCat] = useState(0)
const [userPoints] = useState(1280) // 模拟用户积分
// 获取可兑换商品
const { data, loading } = useRequest(listShopGoods, {
defaultParams: [{ page: 1, limit: 20, exchangeType: 1 }], // exchangeType=1 表示积分兑换商品
onError: (err) => {
Taro.showToast({ title: err.message || '加载失败', icon: 'none' })
}
})
// 模拟兑换商品数据实际应该从API获取
const exchangeProducts = [
{
id: 1,
name: '满100减10优惠券',
points: 500,
image: '',
stock: 100,
type: 1,
description: '积分兑换专属优惠券',
},
{
id: 2,
name: '满200减25优惠券',
points: 1000,
image: '',
stock: 50,
type: 1,
description: '积分兑换专属优惠券',
},
{
id: 3,
name: '品牌定制水杯',
points: 2000,
image: '',
stock: 30,
type: 2,
description: '高品质保温水杯,限量兑换',
},
{
id: 4,
name: '精美帆布袋',
points: 1500,
image: '',
stock: 50,
type: 2,
description: '环保帆布袋,时尚实用',
},
{
id: 5,
name: '视频会员月卡',
points: 3000,
image: '',
stock: 20,
type: 3,
description: '主流视频平台会员月卡',
},
]
// 筛选商品
const filteredProducts = activeCat === 0
? exchangeProducts
: exchangeProducts.filter(p => p.type === activeCat)
// 处理兑换
const handleExchange = (product: typeof exchangeProducts[0]) => {
if (userPoints < product.points) {
Taro.showToast({ title: '积分不足', icon: 'none' })
return
}
if (product.stock <= 0) {
Taro.showToast({ title: '库存不足', icon: 'none' })
return
}
Taro.showModal({
title: '确认兑换',
content: `确定使用 ${product.points} 积分兑换「${product.name}」吗?`,
confirmText: '确认兑换',
confirmColor: '#0e932e',
success: (res) => {
if (res.confirm) {
Taro.showLoading({ title: '兑换中...' })
// 这里应该调用兑换API
setTimeout(() => {
Taro.hideLoading()
Taro.showToast({ title: '兑换成功', icon: 'success' })
}, 1500)
}
}
})
}
// 获取类型标签
const getTypeLabel = (type: number) => {
const map: Record<number, string> = { 1: '优惠券', 2: '实物商品', 3: '虚拟商品' }
return map[type] || '其他'
}
return (
<View className='min-h-screen bg-gray-50'>
{/* 用户积分信息 */}
<View className='p-4' style={{ background: 'linear-gradient(to right, #fb923c, #ef4444)' }}>
<View className='rounded-xl p-4' style={{ backgroundColor: 'rgba(255,255,255,0.2)' }}>
<Text className='text-white text-sm block mb-2'></Text>
<View className='flex items-baseline gap-1'>
<Text className='text-white text-4xl font-bold'>{userPoints}</Text>
<Text className='text-white text-sm opacity-80'></Text>
</View>
<View className='flex gap-4 mt-3'>
<View className='flex-1 rounded-lg p-2 text-center' style={{ backgroundColor: 'rgba(255,255,255,0.3)' }}>
<Text className='text-white text-xs block'></Text>
<Text className='text-white text-sm font-bold block mt-1'>3 </Text>
</View>
<View className='flex-1 rounded-lg p-2 text-center' style={{ backgroundColor: 'rgba(255,255,255,0.3)' }}>
<Text className='text-white text-xs block'></Text>
<Text className='text-white text-sm font-bold block mt-1'></Text>
</View>
</View>
</View>
</View>
{/* 分类标签 */}
<View className='bg-white flex overflow-x-auto'>
{categories.map(cat => (
<View
key={cat.value}
className={`flex-shrink-0 px-4 py-3 relative ${
activeCat === cat.value ? 'text-orange-500 font-medium' : 'text-gray-600'
}`}
onClick={() => setActiveCat(cat.value)}
>
<Text className='text-sm'>{cat.label}</Text>
{activeCat === cat.value && (
<View className='absolute bottom-0 left-0 right-0 flex justify-center'>
<View className='w-8 h-px bg-orange-500 rounded' />
</View>
)}
</View>
))}
</View>
{/* 商品列表 */}
<ScrollView className='flex-1'>
{filteredProducts.length === 0 ? (
<View className='text-center py-16'>
<Text className='text-4xl mb-3 block'>🎁</Text>
<Text className='text-sm text-gray-400'></Text>
</View>
) : (
<View className='p-3'>
{filteredProducts.map(product => {
const canExchange = userPoints >= product.points && product.stock > 0
return (
<View key={product.id} className='bg-white rounded-xl p-4 mb-3 shadow-sm'>
{/* 商品信息 */}
<View className='flex gap-3'>
{/* 商品图片 */}
<View className='w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0'>
<Text className='text-3xl'>🎁</Text>
</View>
{/* 商品详情 */}
<View className='flex-1'>
<Text className='text-sm text-gray-800 font-medium block mb-1'>{product.name}</Text>
<Text className='text-xs text-gray-400 block mb-2'>{product.description}</Text>
<View className='flex items-center gap-2'>
<View className='bg-orange-50 px-2 py-1 rounded'>
<Text className='text-xs text-orange-500 font-bold'>{product.points} </Text>
</View>
<View className='bg-blue-50 px-2 py-1 rounded'>
<Text className='text-xs text-blue-500'>{getTypeLabel(product.type)}</Text>
</View>
</View>
<View className='flex justify-between items-center mt-2'>
<Text className='text-xs text-gray-400'>: {product.stock}</Text>
<View
className={`px-3 py-1 rounded-full ${
canExchange ? 'bg-orange-500' : 'bg-gray-300'
}`}
onClick={() => canExchange && handleExchange(product)}
>
<Text className='text-xs text-white'>
{!canExchange
? userPoints < product.points ? '积分不足' : '库存不足'
: '立即兑换'
}
</Text>
</View>
</View>
</View>
</View>
</View>
)
})}
</View>
)}
</ScrollView>
</View>
)
}
export default ExchangePage