Files
xinlong-shop-taro/src_bak/pages/user/coupon-list.tsx
赵忠林 1fa58040f3 feat(user): 新增收货地址管理及售后申请页面
- 新增地址类型定义,增强前端地址数据结构
- 新增地址编辑页面,支持地址智能识别和定位选点功能
- 地址编辑支持省市区选择及默认地址设置
- 新增地址列表页面,支持地址展示、删除、编辑和选择功能
- 实现售后申请页面,支持选择售后类型和退款原因
- 售后申请支持商品选择、退款金额计算和凭证上传
- 新增售后详情页面,支持售后状态展示及申请取消
- 优化页面加载和用户交互体验,增加错误提示和权限处理
2026-07-01 12:11:56 +08:00

260 lines
7.2 KiB
TypeScript

import React, { useState, useEffect, useCallback } from 'react'
import { View, Text, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { pageShopUserCoupon, removeShopUserCoupon } from '@/api/shop/shopUserCoupon'
import type { ShopUserCoupon, ShopUserCouponParam } from '@/api/shop/shopUserCoupon/model'
import CouponCard from '@/components/business/CouponCard'
import EmptyState from '@/components/common/EmptyState'
import LoadMore from '@/components/common/LoadMore'
definePageConfig({
navigationBarTitleText: '我的优惠券',
})
// Tab 配置
type CouponTab = 'available' | 'used' | 'expired'
const tabList: { title: string; key: CouponTab }[] = [
{ title: '未使用', key: 'available' },
{ title: '已使用', key: 'used' },
{ title: '已过期', key: 'expired' },
]
interface TabState {
coupons: ShopUserCoupon[]
page: number
finished: boolean
loading: boolean
initialized: boolean
}
const CouponListPage: React.FC = () => {
// 根据 URL 参数初始化 tab
const { tab: initTab } = Taro.getCurrentInstance().router?.params || {}
const getInitTabIndex = () => {
switch (initTab) {
case 'used': return 1
case 'expired': return 2
default: return 0
}
}
const [tabIndex, setTabIndex] = useState(getInitTabIndex())
// 每个 tab 独立维护状态
const [tabStates, setTabStates] = useState<TabState[]>(
tabList.map(() => ({
coupons: [],
page: 1,
finished: false,
loading: false,
initialized: false,
}))
)
const currentState = tabStates[tabIndex]
const updateTabState = useCallback(
(index: number, partial: Partial<TabState>) => {
setTabStates(prev => {
const next = [...prev]
next[index] = { ...next[index], ...partial }
return next
})
},
[]
)
const loadCoupons = useCallback(
async (targetIndex: number, p: number) => {
const state = tabStates[targetIndex]
if (state.loading) return
updateTabState(targetIndex, { loading: true })
try {
const tabKey = tabList[targetIndex].key
const params: ShopUserCouponParam = { page: p, limit: 10 }
// 根据 tab 设置筛选条件
// status: 0=未使用, 1=已使用, 2=已过期
switch (tabKey) {
case 'available':
params.status = 0
break
case 'used':
params.status = 1
break
case 'expired':
params.status = 2
break
}
const res = await pageShopUserCoupon(params)
const list = res?.list || []
updateTabState(targetIndex, {
coupons: p === 1 ? list : [...state.coupons, ...list],
page: p,
finished: list.length < 10,
loading: false,
initialized: true,
})
} catch {
updateTabState(targetIndex, { loading: false, initialized: true })
}
},
[tabStates, updateTabState]
)
// 切换 tab 时加载数据
useEffect(() => {
if (!tabStates[tabIndex].initialized) {
loadCoupons(tabIndex, 1)
}
}, [tabIndex])
// 加载更多
const handleLoadMore = () => {
if (!currentState.finished && !currentState.loading) {
loadCoupons(tabIndex, currentState.page + 1)
}
}
// 切换 tab
const handleTabChange = (index: number) => {
if (index !== tabIndex) {
setTabIndex(index)
}
}
// 删除优惠券
const handleDelete = (id: string) => {
Taro.showModal({
title: '提示',
content: '确定要删除该优惠券吗?',
confirmColor: '#0e932e',
success: async (res) => {
if (res.confirm) {
try {
await removeShopUserCoupon(Number(id))
// 从当前 tab 移除
updateTabState(tabIndex, {
coupons: tabStates[tabIndex].coupons.filter(c => c.id !== id)
})
Taro.showToast({ title: '删除成功', icon: 'success' })
} catch {
Taro.showToast({ title: '删除失败', icon: 'none' })
}
}
}
})
}
// 获取空状态文本
const getEmptyText = () => {
switch (tabList[tabIndex].key) {
case 'available': return '暂无可用优惠券'
case 'used': return '暂无已使用优惠券'
case 'expired': return '暂无已过期优惠券'
}
}
// 获取空状态操作
const getEmptyAction = () => {
if (tabList[tabIndex].key === 'available') {
return {
text: '去领取',
action: () => Taro.switchTab({ url: '/pages/index/index' })
}
}
return null
}
// 判断是否禁用
const isDisabled = () => {
const key = tabList[tabIndex].key
return key === 'used' || key === 'expired'
}
return (
<View className='min-h-screen bg-gray-50 flex flex-col'>
{/* 自定义 Tabs 标题栏 */}
<View className='bg-white flex flex-row items-center border-b border-gray-100'>
{tabList.map((tab, index) => (
<View
key={tab.key}
className='flex-1 flex flex-col items-center justify-center py-3'
onClick={() => handleTabChange(index)}
>
<Text
className='text-sm font-medium'
style={{
color: index === tabIndex ? '#ee0a24' : '#666',
}}
>
{tab.title}
</Text>
{index === tabIndex && (
<View
className='mt-1 rounded-full'
style={{
width: '20px',
height: '3px',
backgroundColor: '#ee0a24',
}}
/>
)}
</View>
))}
</View>
{/* 内容区域 - 每个 tab 独立渲染 */}
<View className='flex-1 relative'>
{tabList.map((tab, idx) => (
<View
key={tab.key}
className='absolute inset-0'
style={{
display: idx === tabIndex ? 'flex' : 'none',
flexDirection: 'column',
}}
>
<ScrollView
scrollY
className='flex-1'
onScrollToLower={handleLoadMore}
lowerThreshold={100}
>
<View className='p-3'>
{tabStates[idx].coupons.length > 0 ? (
tabStates[idx].coupons.map(coupon => (
<CouponCard
key={coupon.id}
coupon={coupon}
disabled={isDisabled()}
showDelete={isDisabled()}
onDelete={() => handleDelete(coupon.id!)}
/>
))
) : tabStates[idx].initialized && !tabStates[idx].loading ? (
<EmptyState
text={getEmptyText()}
actionText={getEmptyAction()?.text}
onAction={getEmptyAction()?.action}
/>
) : null}
<LoadMore
loading={tabStates[idx].loading}
finished={tabStates[idx].finished}
/>
</View>
</ScrollView>
</View>
))}
</View>
</View>
)
}
export default CouponListPage