Files
websopy-taro/src/components/CouponCard.tsx
赵忠林 07f5c92f4b feat(add): 新增多页面新增和编辑表单功能
- 添加编辑和新增收货地址页面,支持表单数据加载和提交
- 新增应用密钥凭证、新增应用操作动态、新增应用成员、新增应用版本页面配置
- 实现文章新增及编辑页面,包含图片上传及多种文章属性配置
- 增加注册会员页面,支持头像上传、手机号获取和邀请人关系处理
- 引入统一表单提交成功和失败处理,支持编辑模式数据回显
- 配置统一eslint和editorconfig规则,增强代码规范和编辑体验
- 新增.gitignore规则,屏蔽无关文件和目录,优化版本管理
2026-04-11 12:22:29 +08:00

235 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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