Files
mp-10550/src/coupon/index.tsx
赵忠林 41702c295a refactor(request): 移除旧版请求工具并优化错误处理- 删除了 request-legacy.ts及相关文件
- 更新了所有 API 文件的导入路径
-优化了请求工具的错误处理逻辑
- 移除了冗余的调试信息
- 保留了关键的错误信息
2025-08-22 13:38:38 +08:00

430 lines
11 KiB
TypeScript

import {useState} from "react";
import Taro, {useDidShow} from '@tarojs/taro'
import {
Button,
Empty,
ConfigProvider,
SearchBar,
InfiniteLoading,
Loading,
PullToRefresh,
Tabs,
TabPane,
Swiper,
SwiperItem
} from '@nutui/nutui-react-taro'
import {Filter, Board, Gift} from '@nutui/icons-react-taro'
import {View} from '@tarojs/components'
import {ShopCoupon} from "@/api/shop/shopCoupon/model";
import {pageShopCoupon, receiveCoupon} from "@/api/shop/shopCoupon";
import CouponList from "@/components/CouponList";
import CouponGuide from "@/components/CouponGuide";
import CouponFilter from "@/components/CouponFilter";
import {CouponCardProps} from "@/components/CouponCard";
const CouponReceiveCenter = () => {
const [list, setList] = useState<ShopCoupon[]>([])
const [loading, setLoading] = useState(false)
const [hasMore, setHasMore] = useState(true)
const [searchValue, setSearchValue] = useState('')
const [page, setPage] = useState(1)
const [activeTab, setActiveTab] = useState('0') // 0-全部 1-满减券 2-折扣券 3-免费券
const [hotCoupons, setHotCoupons] = useState<ShopCoupon[]>([]) // 热门优惠券
const [showGuide, setShowGuide] = useState(false)
const [showFilter, setShowFilter] = useState(false)
const [filters, setFilters] = useState({
type: [] as number[],
minAmount: undefined as number | undefined,
sortBy: 'createTime' as 'createTime' | 'amount' | 'expireTime',
sortOrder: 'desc' as 'asc' | 'desc'
})
// 获取优惠券类型过滤条件
const getTypeFilter = () => {
switch (String(activeTab)) {
case '0': // 全部
return {}
case '1': // 满减券
return { type: 10 }
case '2': // 折扣券
return { type: 20 }
case '3': // 免费券
return { type: 30 }
default:
return {}
}
}
// 根据传入的值获取类型过滤条件
const getTypeFilterByValue = (value: string | number) => {
switch (String(value)) {
case '0': // 全部
return {}
case '1': // 满减券
return { type: 10 }
case '2': // 折扣券
return { type: 20 }
case '3': // 免费券
return { type: 30 }
default:
return {}
}
}
// 根据类型过滤条件加载优惠券
const loadCouponsByType = async (typeFilter: any) => {
setLoading(true)
try {
const currentPage = 1
// 获取可领取的优惠券(启用状态且未过期)
const res = await pageShopCoupon({
page: currentPage,
limit: 10,
keywords: searchValue,
enabled: 1, // 启用状态
isExpire: 0, // 未过期
...typeFilter
})
console.log('API返回数据:', res)
if (res && res.list) {
setList(res.list)
setHasMore(res.list.length === 10)
setPage(2)
} else {
setList([])
setHasMore(false)
}
} catch (error) {
console.error('获取优惠券失败:', error)
Taro.showToast({
title: '获取优惠券失败',
icon: 'error'
})
} finally {
setLoading(false)
}
}
const reload = async (isRefresh = false) => {
if (isRefresh) {
setPage(1)
setList([])
setHasMore(true)
}
setLoading(true)
try {
const currentPage = isRefresh ? 1 : page
const typeFilter = getTypeFilter()
console.log('reload - 当前activeTab:', activeTab, '类型过滤:', typeFilter)
// 获取可领取的优惠券(启用状态且未过期)
const res = await pageShopCoupon({
page: currentPage,
limit: 10,
keywords: searchValue,
enabled: 1, // 启用状态
isExpire: 0, // 未过期
...typeFilter,
// 应用筛选条件
...(filters.type.length > 0 && { type: filters.type[0] }),
sortBy: filters.sortBy,
sortOrder: filters.sortOrder
})
console.log('reload - API返回数据:', res)
if (res && res.list) {
const newList = isRefresh ? res.list : [...list, ...res.list]
setList(newList)
// 判断是否还有更多数据
setHasMore(res.list.length === 10)
if (!isRefresh) {
setPage(currentPage + 1)
} else {
setPage(2)
}
} else {
setHasMore(false)
}
} 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) => {
console.log('Tab切换到:', value)
setActiveTab(String(value))
setPage(1)
setList([])
setHasMore(true)
// 直接传递类型值,避免异步状态更新问题
const typeFilter = getTypeFilterByValue(value)
console.log('类型过滤条件:', typeFilter)
// 立即加载数据
loadCouponsByType(typeFilter)
}
// 加载热门优惠券
const loadHotCoupons = async () => {
try {
const res = await pageShopCoupon({
page: 1,
limit: 5,
enabled: 1,
isExpire: 0,
sortBy: 'createTime',
sortOrder: 'desc'
})
if (res && res.list) {
setHotCoupons(res.list)
}
} catch (error) {
console.error('获取热门优惠券失败:', error)
}
}
// 转换优惠券数据为CouponCard组件所需格式
const transformCouponData = (coupon: ShopCoupon): CouponCardProps => {
let amount = 0
let type: 10 | 20 | 30 = 10
if (coupon.type === 10) { // 满减券
type = 10
amount = parseFloat(coupon.reducePrice || '0')
} else if (coupon.type === 20) { // 折扣券
type = 20
amount = coupon.discount || 0
} else if (coupon.type === 30) { // 免费券
type = 30
amount = 0
}
return {
id: coupon.id?.toString(),
amount,
type,
status: 0, // 可领取状态
minAmount: parseFloat(coupon.minPrice || '0'),
title: coupon.name || '优惠券',
description: coupon.description,
startTime: coupon.startTime,
endTime: coupon.endTime,
showReceiveBtn: true, // 显示领取按钮
onReceive: () => handleReceiveCoupon(coupon),
theme: getThemeByType(coupon.type)
}
}
// 根据优惠券类型获取主题色
const getThemeByType = (type?: number): 'red' | 'orange' | 'blue' | 'purple' | 'green' => {
switch (type) {
case 10: return 'red' // 满减券-红色
case 20: return 'orange' // 折扣券-橙色
case 30: return 'green' // 免费券-绿色
default: return 'blue'
}
}
// 领取优惠券
const handleReceiveCoupon = async (coupon: ShopCoupon) => {
try {
// 检查是否已登录
const userId = Taro.getStorageSync('UserId')
if (!userId) {
Taro.showToast({
title: '请先登录',
icon: 'error'
})
return
}
// 调用领取接口
await receiveCoupon({
couponId: coupon.id!,
userId: userId
})
Taro.showToast({
title: '领取成功',
icon: 'success'
})
// 刷新列表
reload(true)
} catch (error: any) {
console.error('领取优惠券失败:', error)
Taro.showToast({
title: error.message || '领取失败',
icon: 'error'
})
}
}
// 优惠券点击事件
const handleCouponClick = (_: CouponCardProps, index: number) => {
const originalCoupon = list[index]
if (originalCoupon) {
// 显示优惠券详情
handleCouponDetail(originalCoupon)
}
}
// 显示优惠券详情
const handleCouponDetail = (coupon: ShopCoupon) => {
// 可以显示优惠券详情弹窗或跳转到详情页
Taro.showModal({
title: coupon.name || '优惠券详情',
content: `${coupon.description || ''}
优惠类型:${coupon.type === 10 ? '满减券' : coupon.type === 20 ? '折扣券' : '免费券'}
${coupon.minPrice ? `最低消费:¥${coupon.minPrice}` : ''}
有效期:${coupon.startTime}${coupon.endTime}`,
showCancel: false,
confirmText: '知道了'
})
}
// 筛选条件变更
const handleFiltersChange = (newFilters: any) => {
setFilters(newFilters)
reload(true)
}
// 查看我的优惠券
const handleViewMyCoupons = () => {
Taro.navigateTo({
url: '/user/coupon/index'
})
}
// 加载更多
const loadMore = async () => {
if (!loading && hasMore) {
await reload(false) // 不刷新,追加数据
}
}
useDidShow(() => {
reload(true)
loadHotCoupons()
});
return (
<ConfigProvider>
{/* 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>
<TabPane title="免费券" value="3">
</TabPane>
</Tabs>
</View>
{/* 优惠券列表 */}
<PullToRefresh
onRefresh={handleRefresh}
headHeight={60}
>
<View style={{ height: '600px', overflowY: 'auto' }} id="coupon-scroll">
{list.length === 0 && !loading ? (
<View className="flex flex-col justify-center items-center" style={{height: '500px'}}>
<Empty
description="暂无可领取的优惠券"
style={{backgroundColor: 'transparent'}}
/>
</View>
) : (
<InfiniteLoading
target="coupon-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>
}
>
<CouponList
coupons={list.map(transformCouponData)}
onCouponClick={handleCouponClick}
showEmpty={false}
/>
</InfiniteLoading>
)}
</View>
</PullToRefresh>
{/* 底部提示 */}
{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={handleViewMyCoupons}
>
</Button>
</View>
</View>
)}
{/* 使用指南弹窗 */}
<CouponGuide
visible={showGuide}
onClose={() => setShowGuide(false)}
/>
{/* 筛选弹窗 */}
<CouponFilter
visible={showFilter}
filters={filters}
onFiltersChange={handleFiltersChange}
onClose={() => setShowFilter(false)}
/>
</ConfigProvider>
);
};
export default CouponReceiveCenter;