Browse Source

refactor(shop): 重构优惠券和礼品卡相关功能

- 优化了优惠券和礼品卡的搜索功能
- 重新设计了统计卡片的样式和布局- 改进了优惠券和礼品卡的领取和兑换流程
- 统一了图标样式和大小
- 调整了部分组件的显示逻辑
master
科技小王子 2 weeks ago
parent
commit
89cadc3886
  1. 1
      src/api/index.ts
  2. 4
      src/api/shop/shopCoupon/model/index.ts
  3. 3
      src/app.config.ts
  4. 2
      src/components/CouponCard.tsx
  5. 4
      src/components/CouponStats.tsx
  6. 43
      src/components/GiftCardStats.tsx
  7. 34
      src/user/coupon/index.tsx
  8. 17
      src/user/coupon/receive.tsx
  9. 25
      src/user/gift/index.tsx
  10. 3
      src/user/gift/redeem.config.ts
  11. 6
      src/user/gift/redeem.tsx

1
src/api/index.ts

@ -57,6 +57,5 @@ export interface PageParam {
dateTime?: string; dateTime?: string;
lang?: string; lang?: string;
model?: string; model?: string;
type?: string;
BaseUrl?: string; BaseUrl?: string;
} }

4
src/api/shop/shopCoupon/model/index.ts

@ -65,5 +65,9 @@ export interface ShopCouponParam extends PageParam {
status?: number; status?: number;
isExpire?: number; isExpire?: number;
sortBy?: string; sortBy?: string;
sortOrder?: 'asc' | 'desc';
keywords?: string; keywords?: string;
enabled?: number;
type?: number;
minAmount?: number;
} }

3
src/app.config.ts

@ -40,7 +40,8 @@ export default defineAppConfig({
"wallet/wallet", "wallet/wallet",
"coupon/index", "coupon/index",
"points/points", "points/points",
"gift/index"
"gift/index",
"gift/redeem"
] ]
}, },
{ {

2
src/components/CouponCard.tsx

@ -129,6 +129,8 @@ const CouponCard: React.FC<CouponCardProps> = ({
return '' return ''
} }
console.log(getValidityText)
const themeClass = getThemeClass() const themeClass = getThemeClass()
return ( return (

4
src/components/CouponStats.tsx

@ -25,9 +25,9 @@ const CouponStats: React.FC<CouponStatsProps> = ({
return ( return (
<View className="bg-white mx-4 my-3 rounded-xl p-4"> <View className="bg-white mx-4 my-3 rounded-xl p-4">
<Text className="text-lg font-semibold mb-4 text-gray-800"></Text>
<Text className="font-semibold text-gray-800"></Text>
<View className="flex justify-between">
<View className="flex justify-between mt-2">
{/* 可用优惠券 */} {/* 可用优惠券 */}
<View <View
className="flex-1 text-center py-3 mx-1 bg-red-50 rounded-lg" className="flex-1 text-center py-3 mx-1 bg-red-50 rounded-lg"

43
src/components/GiftCardStats.tsx

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { View, Text } from '@tarojs/components' import { View, Text } from '@tarojs/components'
import { Gift, Voucher, Clock } from '@nutui/icons-react-taro'
import { Gift, Voucher, Clock, Star } from '@nutui/icons-react-taro'
export interface GiftCardStatsProps { export interface GiftCardStatsProps {
/** 可用礼品卡数量 */ /** 可用礼品卡数量 */
@ -16,26 +16,27 @@ export interface GiftCardStatsProps {
} }
const GiftCardStats: React.FC<GiftCardStatsProps> = ({ const GiftCardStats: React.FC<GiftCardStatsProps> = ({
availableCount,
usedCount,
expiredCount,
onStatsClick
}) => {
availableCount,
usedCount,
expiredCount,
totalValue,
onStatsClick
}) => {
const handleStatsClick = (type: 'available' | 'used' | 'expired' | 'total') => { const handleStatsClick = (type: 'available' | 'used' | 'expired' | 'total') => {
onStatsClick?.(type) onStatsClick?.(type)
} }
return ( return (
<View className="bg-white mx-4 my-3 rounded-xl p-3 shadow-sm">
<View className="bg-white mx-4 my-3 rounded-xl p-3 shadow-sm hidden">
{/* 紧凑的统计卡片 - 2x2 网格 */} {/* 紧凑的统计卡片 - 2x2 网格 */}
<View className="grid grid-cols-3 gap-2">
<View className="grid grid-cols-2 gap-2">
{/* 可用礼品卡 */} {/* 可用礼品卡 */}
<View <View
className="flex items-center justify-between p-2 bg-yellow-50 rounded-lg border border-yellow-200"
className="flex items-center justify-between p-3 bg-yellow-50 rounded-lg border border-yellow-200"
onClick={() => handleStatsClick('available')} onClick={() => handleStatsClick('available')}
> >
<View className="flex items-center"> <View className="flex items-center">
<Gift size="16" className="text-yellow-600 mr-1" />
<Gift size="20" className="text-yellow-600 mr-2" />
<Text className="text-sm text-gray-600"></Text> <Text className="text-sm text-gray-600"></Text>
</View> </View>
<Text className="text-lg font-bold text-yellow-600">{availableCount}</Text> <Text className="text-lg font-bold text-yellow-600">{availableCount}</Text>
@ -43,11 +44,11 @@ const GiftCardStats: React.FC<GiftCardStatsProps> = ({
{/* 已使用礼品卡 */} {/* 已使用礼品卡 */}
<View <View
className="flex items-center justify-between p-2 bg-green-50 rounded-lg border border-green-200"
className="flex items-center justify-between p-3 bg-green-50 rounded-lg border border-green-200"
onClick={() => handleStatsClick('used')} onClick={() => handleStatsClick('used')}
> >
<View className="flex items-center"> <View className="flex items-center">
<Voucher size="16" className="text-green-600 mr-1" />
<Voucher size="20" className="text-green-600 mr-2" />
<Text className="text-sm text-gray-600">使</Text> <Text className="text-sm text-gray-600">使</Text>
</View> </View>
<Text className="text-lg font-bold text-green-600">{usedCount}</Text> <Text className="text-lg font-bold text-green-600">{usedCount}</Text>
@ -55,15 +56,29 @@ const GiftCardStats: React.FC<GiftCardStatsProps> = ({
{/* 已过期礼品卡 */} {/* 已过期礼品卡 */}
<View <View
className="flex items-center justify-between p-2 bg-gray-50 rounded-lg border border-gray-200"
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border border-gray-200"
onClick={() => handleStatsClick('expired')} onClick={() => handleStatsClick('expired')}
> >
<View className="flex items-center"> <View className="flex items-center">
<Clock size="16" className="text-gray-500 mr-1" />
<Clock size="20" className="text-gray-500 mr-2" />
<Text className="text-sm text-gray-600"></Text> <Text className="text-sm text-gray-600"></Text>
</View> </View>
<Text className="text-lg font-bold text-gray-500">{expiredCount}</Text> <Text className="text-lg font-bold text-gray-500">{expiredCount}</Text>
</View> </View>
{/* 总价值 */}
{totalValue !== undefined && (
<View
className="flex items-center justify-between p-3 bg-purple-50 rounded-lg border border-purple-200"
onClick={() => handleStatsClick('total')}
>
<View className="flex items-center">
<Star size="20" className="text-purple-600 mr-2" />
<Text className="text-sm text-gray-600"></Text>
</View>
<Text className="text-lg font-bold text-purple-600">¥{totalValue}</Text>
</View>
)}
</View> </View>
</View> </View>
) )

34
src/user/coupon/index.tsx

@ -1,7 +1,7 @@
import {useState} from "react"; import {useState} from "react";
import Taro, {useDidShow} from '@tarojs/taro' import Taro, {useDidShow} from '@tarojs/taro'
import {Button, Empty, ConfigProvider, SearchBar, InfiniteLoading, Loading, PullToRefresh, Tabs, TabPane} from '@nutui/nutui-react-taro' import {Button, Empty, ConfigProvider, SearchBar, InfiniteLoading, Loading, PullToRefresh, Tabs, TabPane} from '@nutui/nutui-react-taro'
import {Gift, Search, Plus, Filter} from '@nutui/icons-react-taro'
import {Plus, Filter} from '@nutui/icons-react-taro'
import {View} from '@tarojs/components' import {View} from '@tarojs/components'
import {ShopCoupon} from "@/api/shop/shopCoupon/model"; import {ShopCoupon} from "@/api/shop/shopCoupon/model";
import {pageShopCoupon} from "@/api/shop/shopCoupon"; import {pageShopCoupon} from "@/api/shop/shopCoupon";
@ -20,6 +20,7 @@ const CouponManage = () => {
const [searchValue, setSearchValue] = useState('') const [searchValue, setSearchValue] = useState('')
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [total, setTotal] = useState(0) const [total, setTotal] = useState(0)
console.log('total = ',total)
const [activeTab, setActiveTab] = useState('0') // 0-可用 1-已使用 2-已过期 const [activeTab, setActiveTab] = useState('0') // 0-可用 1-已使用 2-已过期
const [stats, setStats] = useState({ const [stats, setStats] = useState({
available: 0, available: 0,
@ -191,7 +192,7 @@ const CouponManage = () => {
} }
// 优惠券点击事件 // 优惠券点击事件
const handleCouponClick = (coupon: CouponCardProps, index: number) => {
const handleCouponClick = (_coupon: CouponCardProps, index: number) => {
const originalCoupon = list[index] const originalCoupon = list[index]
if (originalCoupon) { if (originalCoupon) {
// 显示优惠券详情 // 显示优惠券详情
@ -317,15 +318,14 @@ const CouponManage = () => {
return ( return (
<ConfigProvider> <ConfigProvider>
{/* 搜索栏和领取入口 */} {/* 搜索栏和领取入口 */}
<View className="bg-white px-4 py-3">
<View className="bg-white px-4 py-3 hidden">
<View className="flex items-center gap-3"> <View className="flex items-center gap-3">
<View className="flex-1"> <View className="flex-1">
<SearchBar <SearchBar
placeholder="搜索优惠券名称"
placeholder="搜索"
value={searchValue} value={searchValue}
onChange={setSearchValue} onChange={setSearchValue}
onSearch={handleSearch} onSearch={handleSearch}
leftIcon={<Search />}
/> />
</View> </View>
<Button <Button
@ -374,13 +374,6 @@ const CouponManage = () => {
</Tabs> </Tabs>
</View> </View>
{/* 统计信息 */}
{total > 0 && (
<View className="px-4 py-2 text-sm text-gray-500 bg-gray-50">
{total}
</View>
)}
{/* 优惠券列表 */} {/* 优惠券列表 */}
<PullToRefresh <PullToRefresh
onRefresh={handleRefresh} onRefresh={handleRefresh}
@ -425,23 +418,6 @@ const CouponManage = () => {
</View> </View>
</PullToRefresh> </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>
<Button
size="small"
type="primary"
onClick={() => Taro.navigateTo({url: '/pages/index/index'})}
>
</Button>
</View>
)}
{/* 使用指南弹窗 */} {/* 使用指南弹窗 */}
<CouponGuide <CouponGuide
visible={showGuide} visible={showGuide}

17
src/user/coupon/receive.tsx

@ -1,7 +1,7 @@
import {useState} from "react"; import {useState} from "react";
import Taro, {useDidShow} from '@tarojs/taro' import Taro, {useDidShow} from '@tarojs/taro'
import {Button, Empty, ConfigProvider, SearchBar, InfiniteLoading, Loading, PullToRefresh} from '@nutui/nutui-react-taro' import {Button, Empty, ConfigProvider, SearchBar, InfiniteLoading, Loading, PullToRefresh} from '@nutui/nutui-react-taro'
import {Gift, Search} from '@nutui/icons-react-taro'
import {Gift} from '@nutui/icons-react-taro'
import {View} from '@tarojs/components' import {View} from '@tarojs/components'
import {ShopCoupon} from "@/api/shop/shopCoupon/model"; import {ShopCoupon} from "@/api/shop/shopCoupon/model";
import {pageShopCoupon} from "@/api/shop/shopCoupon"; import {pageShopCoupon} from "@/api/shop/shopCoupon";
@ -31,7 +31,7 @@ const CouponReceive = () => {
page: currentPage, page: currentPage,
limit: 10, limit: 10,
keywords: searchValue, keywords: searchValue,
enabled: '1', // 启用状态
enabled: 1, // 启用状态
isExpire: 0 // 未过期 isExpire: 0 // 未过期
}) })
@ -77,7 +77,7 @@ const CouponReceive = () => {
const transformCouponData = (coupon: ShopCoupon): CouponCardProps => { const transformCouponData = (coupon: ShopCoupon): CouponCardProps => {
let amount = 0 let amount = 0
let type: 1 | 2 | 3 = 1 let type: 1 | 2 | 3 = 1
if (coupon.type === 10) { // 满减券 if (coupon.type === 10) { // 满减券
type = 1 type = 1
amount = parseFloat(coupon.reducePrice || '0') amount = parseFloat(coupon.reducePrice || '0')
@ -114,16 +114,16 @@ const CouponReceive = () => {
} }
// 领取优惠券 // 领取优惠券
const handleReceiveCoupon = async (coupon: ShopCoupon) => {
const handleReceiveCoupon = async (_coupon: ShopCoupon) => {
try { try {
// 这里应该调用领取优惠券的API // 这里应该调用领取优惠券的API
// await receiveCoupon(coupon.id) // await receiveCoupon(coupon.id)
Taro.showToast({ Taro.showToast({
title: '领取成功', title: '领取成功',
icon: 'success' icon: 'success'
}) })
// 刷新列表 // 刷新列表
reload(true) reload(true)
} catch (error) { } catch (error) {
@ -136,7 +136,7 @@ const CouponReceive = () => {
} }
// 优惠券点击事件 // 优惠券点击事件
const handleCouponClick = (coupon: CouponCardProps, index: number) => {
const handleCouponClick = (_coupon: CouponCardProps, index: number) => {
const originalCoupon = list[index] const originalCoupon = list[index]
if (originalCoupon) { if (originalCoupon) {
// 显示优惠券详情 // 显示优惠券详情
@ -168,11 +168,10 @@ const CouponReceive = () => {
{/* 搜索栏 */} {/* 搜索栏 */}
<View className="bg-white px-4 py-3"> <View className="bg-white px-4 py-3">
<SearchBar <SearchBar
placeholder="搜索优惠券名称"
placeholder="搜索"
value={searchValue} value={searchValue}
onChange={setSearchValue} onChange={setSearchValue}
onSearch={handleSearch} onSearch={handleSearch}
leftIcon={<Search />}
/> />
</View> </View>

25
src/user/gift/index.tsx

@ -1,7 +1,7 @@
import {useState} from "react"; import {useState} from "react";
import Taro, {useDidShow} from '@tarojs/taro' import Taro, {useDidShow} from '@tarojs/taro'
import {Button, Empty, ConfigProvider, InfiniteLoading, Loading, PullToRefresh, Tabs, TabPane} from '@nutui/nutui-react-taro'
import {Gift, Plus, Board, QrCode} from '@nutui/icons-react-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 {View} from '@tarojs/components'
import {ShopGift} from "@/api/shop/shopGift/model"; import {ShopGift} from "@/api/shop/shopGift/model";
import {getUserGifts} from "@/api/shop/shopGift"; import {getUserGifts} from "@/api/shop/shopGift";
@ -14,7 +14,7 @@ const GiftCardManage = () => {
const [list, setList] = useState<ShopGift[]>([]) const [list, setList] = useState<ShopGift[]>([])
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [hasMore, setHasMore] = useState(true) const [hasMore, setHasMore] = useState(true)
// const [searchValue, setSearchValue] = useState('')
const [searchValue, setSearchValue] = useState('')
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
// const [total, setTotal] = useState(0) // const [total, setTotal] = useState(0)
const [activeTab, setActiveTab] = useState('0') // 0-可用 1-已使用 2-已过期 const [activeTab, setActiveTab] = useState('0') // 0-可用 1-已使用 2-已过期
@ -97,10 +97,10 @@ const GiftCardManage = () => {
} }
// 搜索功能 // 搜索功能
// const handleSearch = (value: string) => {
// setSearchValue(value)
// reload(true)
// }
const handleSearch = (value: string) => {
setSearchValue(value)
reload(true)
}
// 下拉刷新 // 下拉刷新
const handleRefresh = async () => { const handleRefresh = async () => {
@ -272,10 +272,19 @@ const GiftCardManage = () => {
{/* 搜索栏和功能入口 */} {/* 搜索栏和功能入口 */}
<View className="bg-white px-4 py-3"> <View className="bg-white px-4 py-3">
<View className="flex items-center justify-between gap-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 <Button
size="small" size="small"
type="primary" type="primary"
icon={<Plus />}
icon={<Retweet />}
onClick={handleRedeemGift} onClick={handleRedeemGift}
> >

3
src/user/gift/redeem.config.ts

@ -1,6 +1,5 @@
export default definePageConfig({ export default definePageConfig({
navigationBarTitleText: '兑换礼品卡', navigationBarTitleText: '兑换礼品卡',
navigationBarTextStyle: 'black', navigationBarTextStyle: 'black',
navigationBarBackgroundColor: '#ffffff',
navigationStyle: 'custom'
navigationBarBackgroundColor: '#ffffff'
}) })

6
src/user/gift/redeem.tsx

@ -39,7 +39,9 @@ const GiftCardRedeem = () => {
setValidating(true) setValidating(true)
try { try {
const gift = await validateGiftCode(codeToValidate.trim()) const gift = await validateGiftCode(codeToValidate.trim())
setValidGift(gift)
if(gift){
setValidGift(gift)
}
Taro.showToast({ Taro.showToast({
title: '验证成功', title: '验证成功',
icon: 'success' icon: 'success'
@ -138,7 +140,7 @@ const GiftCardRedeem = () => {
return ( return (
<ConfigProvider> <ConfigProvider>
{/* 自定义导航栏 */} {/* 自定义导航栏 */}
<View className="flex items-center justify-between p-4 bg-white border-b border-gray-100">
<View className="flex items-center justify-between p-4 bg-white border-b border-gray-100 hidden">
<View className="flex items-center" onClick={handleBack}> <View className="flex items-center" onClick={handleBack}>
<ArrowLeft size="20" /> <ArrowLeft size="20" />
<Text className="ml-2 text-lg"></Text> <Text className="ml-2 text-lg"></Text>

Loading…
Cancel
Save