451 lines
13 KiB
TypeScript
451 lines
13 KiB
TypeScript
import {useState} from "react";
|
||
import Taro, {useDidShow} from '@tarojs/taro'
|
||
import {Button, Empty, ConfigProvider,SearchBar, InfiniteLoading, Loading, PullToRefresh, Tabs, TabPane} from '@nutui/nutui-react-taro'
|
||
import {Gift, Retweet, Board, QrCode} from '@nutui/icons-react-taro'
|
||
import {View} from '@tarojs/components'
|
||
import {ShopGift} from "@/api/shop/shopGift/model";
|
||
import {getUserGifts} from "@/api/shop/shopGift";
|
||
import GiftCardList from "@/components/GiftCardList";
|
||
import GiftCardStats from "@/components/GiftCardStats";
|
||
import GiftCardGuide from "@/components/GiftCardGuide";
|
||
import {GiftCardProps} from "@/components/GiftCard";
|
||
|
||
const GiftCardManage = () => {
|
||
const [list, setList] = useState<ShopGift[]>([])
|
||
const [loading, setLoading] = useState(false)
|
||
const [hasMore, setHasMore] = useState(true)
|
||
const [searchValue, setSearchValue] = useState('')
|
||
const [page, setPage] = useState(1)
|
||
// const [total, setTotal] = useState(0)
|
||
const [activeTab, setActiveTab] = useState<string | number>('0') // 0-可用 1-已使用 2-已过期
|
||
const [stats, setStats] = useState({
|
||
available: 0,
|
||
used: 0,
|
||
expired: 0,
|
||
totalValue: 0
|
||
})
|
||
const [showGuide, setShowGuide] = useState(false)
|
||
// const [showRedeemModal, setShowRedeemModal] = useState(false)
|
||
// const [filters, setFilters] = useState({
|
||
// type: [] as number[],
|
||
// sortBy: 'createTime' as 'createTime' | 'expireTime' | 'faceValue' | 'useTime',
|
||
// sortOrder: 'desc' as 'asc' | 'desc'
|
||
// })
|
||
|
||
// 获取礼品卡状态过滤条件
|
||
const getStatusFilter = () => {
|
||
switch (String(activeTab)) {
|
||
case '0': // 可用
|
||
return { useStatus: 0 }
|
||
case '1': // 已使用
|
||
return { useStatus: 1 }
|
||
case '2': // 已过期
|
||
return { useStatus: 2 }
|
||
default:
|
||
return {}
|
||
}
|
||
}
|
||
|
||
const reload = async (isRefresh = false) => {
|
||
if (isRefresh) {
|
||
setPage(1)
|
||
setList([])
|
||
setHasMore(true)
|
||
}
|
||
|
||
setLoading(true)
|
||
try {
|
||
const currentPage = isRefresh ? 1 : page
|
||
const statusFilter = getStatusFilter()
|
||
const res = await getUserGifts({
|
||
page: currentPage,
|
||
limit: 10,
|
||
userId: Taro.getStorageSync('UserId'),
|
||
// keywords: searchValue,
|
||
...statusFilter,
|
||
// 应用筛选条件
|
||
// ...(filters.type.length > 0 && { type: filters.type[0] }),
|
||
// sortBy: filters.sortBy,
|
||
// sortOrder: filters.sortOrder
|
||
})
|
||
console.log(res?.list,'>>>>lalala')
|
||
if (res && res.list) {
|
||
const newList = isRefresh ? res.list : [...list, ...res.list]
|
||
setList(newList)
|
||
// setTotal(res.count || 0)
|
||
|
||
// 判断是否还有更多数据
|
||
setHasMore(res.list.length === 10) // 如果返回的数据等于limit,说明可能还有更多
|
||
|
||
if (!isRefresh) {
|
||
setPage(currentPage + 1)
|
||
} else {
|
||
setPage(2) // 刷新后下一页是第2页
|
||
}
|
||
} else {
|
||
setHasMore(false)
|
||
// setTotal(0)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取礼品卡失败:', error)
|
||
Taro.showToast({
|
||
title: '获取礼品卡失败',
|
||
icon: 'error'
|
||
});
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
// 搜索功能
|
||
const handleSearch = (value: string) => {
|
||
setSearchValue(value)
|
||
reload(true)
|
||
}
|
||
|
||
// 下拉刷新
|
||
const handleRefresh = async () => {
|
||
await reload(true)
|
||
}
|
||
|
||
// Tab切换
|
||
const handleTabChange = (value: string | number) => {
|
||
setActiveTab(value)
|
||
setPage(1)
|
||
setList([])
|
||
setHasMore(true)
|
||
// 延迟执行reload,确保状态更新完成
|
||
setTimeout(() => {
|
||
reload(true)
|
||
}, 100)
|
||
}
|
||
|
||
// 转换礼品卡数据为GiftCard组件所需格式
|
||
const transformGiftData = (gift: ShopGift): GiftCardProps => {
|
||
return {
|
||
id: gift.id || 0,
|
||
name: gift.name || '礼品卡', // 礼品卡名称
|
||
goodsName: gift.goodsName, // 商品名称(新增字段)
|
||
description: gift.description || gift.instructions, // 使用说明作为描述
|
||
code: gift.code,
|
||
goodsImage: gift.goodsImage, // 商品图片
|
||
faceValue: gift.faceValue,
|
||
type: gift.type,
|
||
useStatus: gift.useStatus,
|
||
expireTime: gift.expireTime,
|
||
useTime: gift.useTime,
|
||
useLocation: gift.useLocation,
|
||
contactInfo: gift.contactInfo,
|
||
// 添加商品信息
|
||
goodsInfo: {
|
||
// 如果有商品名称或商品ID,说明是关联商品的礼品卡
|
||
...((gift.goodsName || gift.goodsId) && {
|
||
specification: `礼品卡面值:¥${gift.faceValue}`,
|
||
category: getTypeText(gift.type),
|
||
tags: [
|
||
getTypeText(gift.type),
|
||
gift.useStatus === 0 ? '可使用' : gift.useStatus === 1 ? '已使用' : '已过期',
|
||
...(gift.goodsName ? ['商品礼品卡'] : [])
|
||
].filter(Boolean),
|
||
instructions: gift.instructions ? [gift.instructions] : [
|
||
'请在有效期内使用',
|
||
'出示兑换码即可使用',
|
||
'不可兑换现金',
|
||
...(gift.goodsName ? ['此礼品卡关联具体商品'] : [])
|
||
],
|
||
notices: [
|
||
'礼品卡一经使用不可退换',
|
||
'请妥善保管兑换码',
|
||
'如有疑问请联系客服',
|
||
...(gift.goodsName ? ['商品以实际为准'] : [])
|
||
]
|
||
})
|
||
},
|
||
showCode: gift.useStatus === 0, // 只有可用状态显示兑换码
|
||
showUseBtn: gift.useStatus === 0, // 只有可用状态显示使用按钮
|
||
showDetailBtn: true,
|
||
showGoodsDetail: true, // 显示商品详情
|
||
theme: getThemeByType(gift.type),
|
||
onUse: () => handleUseGift(gift),
|
||
onDetail: () => handleGiftDetail(gift)
|
||
}
|
||
}
|
||
|
||
// 获取礼品卡类型文本
|
||
const getTypeText = (type?: number): string => {
|
||
switch (type) {
|
||
case 10: return '实物礼品卡'
|
||
case 20: return '虚拟礼品卡'
|
||
case 30: return '服务礼品卡'
|
||
default: return '礼品卡'
|
||
}
|
||
}
|
||
|
||
// 根据礼品卡类型获取主题色
|
||
const getThemeByType = (type?: number): 'gold' | 'silver' | 'bronze' | 'blue' | 'green' | 'purple' => {
|
||
switch (type) {
|
||
case 10: return 'gold' // 实物礼品卡
|
||
case 20: return 'blue' // 虚拟礼品卡
|
||
case 30: return 'green' // 服务礼品卡
|
||
default: return 'silver'
|
||
}
|
||
}
|
||
|
||
// 使用礼品卡
|
||
const handleUseGift = (gift: ShopGift) => {
|
||
Taro.showModal({
|
||
title: '使用礼品卡',
|
||
content: `确定要使用"${gift.name}"吗?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 跳转到礼品卡使用页面
|
||
Taro.navigateTo({
|
||
url: `/user/gift/use?id=${gift.id}`
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 礼品卡点击事件
|
||
const handleGiftClick = (gift: GiftCardProps, index: number) => {
|
||
console.log(gift.code)
|
||
const originalGift = list[index]
|
||
if (originalGift) {
|
||
// 显示礼品卡详情
|
||
handleGiftDetail(originalGift)
|
||
}
|
||
}
|
||
|
||
// 显示礼品卡详情
|
||
const handleGiftDetail = (gift: ShopGift) => {
|
||
// 跳转到礼品卡详情页
|
||
Taro.navigateTo({
|
||
url: `/user/gift/detail?id=${gift.id}`
|
||
})
|
||
}
|
||
|
||
// 加载礼品卡统计数据
|
||
// const loadGiftStats = async () => {
|
||
// try {
|
||
// // 并行获取各状态的礼品卡数量
|
||
// const [availableRes, usedRes, expiredRes] = await Promise.all([
|
||
// getUserGifts({ page: 1, limit: 1, useStatus: 0 }),
|
||
// getUserGifts({ page: 1, limit: 1, useStatus: 1 }),
|
||
// getUserGifts({ page: 1, limit: 1, useStatus: 2 })
|
||
// ])
|
||
//
|
||
// // 计算总价值(仅可用礼品卡)
|
||
// const availableGifts = await getUserGifts({ page: 1, limit: 100, useStatus: 0 })
|
||
// const totalValue = availableGifts?.list?.reduce((sum, gift) => {
|
||
// return sum + parseFloat(gift.faceValue || '0')
|
||
// }, 0) || 0
|
||
//
|
||
// setStats({
|
||
// available: availableRes?.count || 0,
|
||
// used: usedRes?.count || 0,
|
||
// expired: expiredRes?.count || 0,
|
||
// totalValue
|
||
// })
|
||
// } catch (error) {
|
||
// console.error('获取礼品卡统计失败:', error)
|
||
// }
|
||
// }
|
||
|
||
// 统计卡片点击事件
|
||
const handleStatsClick = (type: 'available' | 'used' | 'expired' | 'total') => {
|
||
const tabMap = {
|
||
available: '0',
|
||
used: '1',
|
||
expired: '2',
|
||
total: '0' // 总价值点击跳转到可用礼品卡
|
||
}
|
||
if (tabMap[type]) {
|
||
handleTabChange(tabMap[type])
|
||
}
|
||
}
|
||
|
||
// 兑换礼品卡
|
||
const handleRedeemGift = () => {
|
||
Taro.navigateTo({
|
||
url: '/user/gift/redeem'
|
||
})
|
||
}
|
||
|
||
// 扫码兑换礼品卡
|
||
const handleScanRedeem = () => {
|
||
Taro.scanCode({
|
||
success: (res) => {
|
||
// 处理扫码结果
|
||
const code = res.result
|
||
if (code) {
|
||
Taro.navigateTo({
|
||
url: `/user/gift/redeem?code=${encodeURIComponent(code)}`
|
||
})
|
||
}
|
||
},
|
||
fail: () => {
|
||
Taro.showToast({
|
||
title: '扫码失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 加载更多
|
||
const loadMore = async () => {
|
||
if (!loading && hasMore) {
|
||
await reload(false) // 不刷新,追加数据
|
||
}
|
||
}
|
||
|
||
useDidShow(() => {
|
||
reload(true).then()
|
||
// loadGiftStats().then()
|
||
});
|
||
|
||
return (
|
||
<ConfigProvider>
|
||
{/* 搜索栏和功能入口 */}
|
||
<View className="bg-white px-4 py-3">
|
||
<View className="flex items-center justify-between gap-3">
|
||
<View className="flex-1 hidden">
|
||
<SearchBar
|
||
placeholder="搜索"
|
||
value={searchValue}
|
||
className={'border'}
|
||
onChange={setSearchValue}
|
||
onSearch={handleSearch}
|
||
/>
|
||
</View>
|
||
<Button
|
||
size="small"
|
||
type="primary"
|
||
icon={<Retweet />}
|
||
onClick={handleRedeemGift}
|
||
>
|
||
兑换
|
||
</Button>
|
||
<Button
|
||
size="small"
|
||
fill="outline"
|
||
icon={<QrCode />}
|
||
onClick={handleScanRedeem}
|
||
>
|
||
扫码
|
||
</Button>
|
||
<Button
|
||
size="small"
|
||
fill="outline"
|
||
icon={<Board />}
|
||
onClick={() => setShowGuide(true)}
|
||
>
|
||
帮助
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 礼品卡统计 */}
|
||
<GiftCardStats
|
||
availableCount={stats.available}
|
||
usedCount={stats.used}
|
||
expiredCount={stats.expired}
|
||
totalValue={stats.totalValue}
|
||
onStatsClick={handleStatsClick}
|
||
/>
|
||
|
||
{/* Tab切换 */}
|
||
<View className="bg-white">
|
||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||
<TabPane title="可用" value="0">
|
||
</TabPane>
|
||
<TabPane title="已使用" value="1">
|
||
</TabPane>
|
||
<TabPane title="已过期" value="2">
|
||
</TabPane>
|
||
</Tabs>
|
||
</View>
|
||
|
||
{/* 礼品卡列表 */}
|
||
<PullToRefresh
|
||
onRefresh={handleRefresh}
|
||
headHeight={60}
|
||
>
|
||
<View style={{ height: '600px', overflowY: 'auto' }} id="gift-scroll">
|
||
{list.length === 0 && !loading ? (
|
||
<View className="flex flex-col justify-center items-center" style={{height: '500px'}}>
|
||
<Empty
|
||
description={
|
||
activeTab === '0' ? "暂无可用礼品卡" :
|
||
activeTab === '1' ? "暂无已使用礼品卡" :
|
||
"暂无已过期礼品卡"
|
||
}
|
||
style={{backgroundColor: 'transparent'}}
|
||
/>
|
||
</View>
|
||
) : (
|
||
<InfiniteLoading
|
||
target="gift-scroll"
|
||
hasMore={hasMore}
|
||
onLoadMore={loadMore}
|
||
loadingText={
|
||
<View className="flex justify-center items-center py-4">
|
||
<Loading />
|
||
<View className="ml-2">加载中...</View>
|
||
</View>
|
||
}
|
||
loadMoreText={
|
||
<View className="text-center py-4 text-gray-500">
|
||
{list.length === 0 ? "暂无数据" : "没有更多了"}
|
||
</View>
|
||
}
|
||
>
|
||
<GiftCardList
|
||
gifts={list.map(transformGiftData)}
|
||
onGiftClick={handleGiftClick}
|
||
showEmpty={false}
|
||
/>
|
||
</InfiniteLoading>
|
||
)}
|
||
</View>
|
||
</PullToRefresh>
|
||
|
||
{/* 底部提示 */}
|
||
{activeTab === '0' && list.length === 0 && !loading && (
|
||
<View className="text-center py-8">
|
||
<View className="text-gray-400 mb-4">
|
||
<Gift size="48" />
|
||
</View>
|
||
<View className="text-gray-500 mb-2">暂无可用礼品卡</View>
|
||
<View className="flex gap-2 justify-center">
|
||
<Button
|
||
size="small"
|
||
type="primary"
|
||
onClick={handleRedeemGift}
|
||
>
|
||
兑换礼品卡
|
||
</Button>
|
||
<Button
|
||
size="small"
|
||
fill="outline"
|
||
onClick={handleScanRedeem}
|
||
>
|
||
扫码兑换
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* 使用指南弹窗 */}
|
||
<GiftCardGuide
|
||
visible={showGuide}
|
||
onClose={() => setShowGuide(false)}
|
||
/>
|
||
</ConfigProvider>
|
||
);
|
||
|
||
};
|
||
|
||
export default GiftCardManage;
|