时里院子市集
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

234 lines
5.9 KiB

import React from 'react'
import { View, Text } from '@tarojs/components'
import { Button } from '@nutui/nutui-react-taro'
import './CouponCard.scss'
export interface CouponCardProps {
/** 优惠券ID */
id?: string
/** 优惠券金额 */
amount: number
/** 最低消费金额 */
minAmount?: number
/** 优惠券类型:10-满减券 20-折扣券 30-免费券 */
type?: 10 | 20 | 30
/** 优惠券状态:0-未使用 1-已使用 2-已过期 */
status?: 0 | 1 | 2
/** 状态文本描述(后端返回) */
statusText?: string
/** 优惠券标题 */
title?: string
/** 优惠券描述 */
description?: string
/** 有效期开始时间 */
startTime?: string
/** 有效期结束时间 */
endTime?: string
/** 是否即将过期(后端计算) */
isExpiringSoon?: boolean
/** 剩余天数(后端计算) */
daysRemaining?: number
/** 剩余小时数(后端计算) */
hoursRemaining?: number
/** 是否显示领取按钮 */
showReceiveBtn?: boolean
/** 是否显示使用按钮 */
showUseBtn?: boolean
/** 领取按钮点击事件 */
onReceive?: () => void
/** 使用按钮点击事件 */
onUse?: () => void
/** 优惠券样式主题:red | orange | blue | purple | green */
theme?: 'red' | 'orange' | 'blue' | 'purple' | 'green'
}
const CouponCard: React.FC<CouponCardProps> = ({
amount,
minAmount,
type = 10,
status = 0,
statusText,
title,
startTime,
endTime,
isExpiringSoon,
daysRemaining,
hoursRemaining,
showReceiveBtn = false,
showUseBtn = false,
onReceive,
onUse,
theme = 'red'
}) => {
// 获取主题颜色类名
const getThemeClass = () => {
return `theme-${theme}`
}
// 格式化优惠券金额显示
const formatAmount = () => {
switch (type) {
case 10: // 满减券
return `¥${amount}`
case 20: // 折扣券
return `${amount}`
case 30: // 免费券
return '免费'
default:
return `¥${amount}`
}
}
// 获取优惠券状态文本
const getStatusText = () => {
// 优先使用后端返回的状态文本
if (statusText) {
return statusText
}
// 兜底逻辑
switch (status) {
case 0:
return '可用'
case 1:
return '已使用'
case 2:
return '已过期'
default:
return '可用'
}
}
// 获取使用条件文本
const getConditionText = () => {
if (type === 30) return '免费使用' // 免费券
if (minAmount && minAmount > 0) {
return `${minAmount}元可用`
}
return '无门槛'
}
// 格式化有效期显示
const formatValidityPeriod = () => {
// 第一优先级:使用后端返回的状态文本
if (statusText) {
return statusText
}
// 第二优先级:根据状态码显示
if (status === 2) {
return '已过期'
}
if (status === 1) {
return '已使用'
}
// 第三优先级:使用后端计算的剩余时间
if (isExpiringSoon && daysRemaining !== undefined) {
if (daysRemaining <= 0 && hoursRemaining !== undefined) {
return `${hoursRemaining}小时后过期`
}
return `${daysRemaining}天后过期`
}
// 兜底逻辑:使用前端计算
if (!endTime) return '可用'
const end = new Date(endTime)
const now = new Date()
if (startTime) {
const start = new Date(startTime)
// 如果还未开始
if (now < start) {
return `${start.getMonth() + 1}.${start.getDate()} 开始生效`
}
}
// 计算剩余天数
const diffTime = end.getTime() - now.getTime()
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
if (diffDays <= 0) {
return '已过期'
} else if (diffDays <= 3) {
return `${diffDays}天后过期`
} else {
return `${end.getMonth() + 1}.${end.getDate()} 过期`
}
}
const themeClass = getThemeClass()
return (
<View className={`coupon-card ${status !== 0 ? 'disabled' : ''}`}>
{/* 左侧金额区域 */}
<View className={`coupon-left ${themeClass}`}>
<View className="amount-wrapper">
{type !== 30 && <Text className="currency">¥</Text>}
<Text className="amount">{formatAmount()}</Text>
</View>
<View className="condition">
{getConditionText()}
</View>
</View>
{/* 中间分割线 */}
<View className="coupon-divider">
<View className="divider-line"></View>
<View className="divider-circle-top"></View>
<View className="divider-circle-bottom"></View>
</View>
{/* 右侧信息区域 */}
<View className="coupon-right">
<View className="coupon-info">
<View className="coupon-title">
{title || (type === 10 ? '满减券' : type === 20 ? '折扣券' : '免费券')}
</View>
<View className="coupon-validity">
{formatValidityPeriod()}
</View>
</View>
{/* 按钮区域 */}
<View className="coupon-actions">
{showReceiveBtn && status === 0 && (
<Button
className={`coupon-btn ${themeClass}`}
size="small"
onClick={onReceive}
>
</Button>
)}
{showUseBtn && status === 0 && (
<Button
className={`coupon-btn ${themeClass}`}
size="small"
onClick={onUse}
>
使
</Button>
)}
{status !== 0 && (
<View className="status-text">
{getStatusText()}
</View>
)}
</View>
</View>
{/* 状态遮罩 */}
{status !== 0 && (
<View className="status-overlay">
<Text className="status-badge">
{getStatusText()}
</Text>
</View>
)}
</View>
)
}
export default CouponCard