forked from gxwebsoft/mp-10550
feat(payment): 更新支付倒计时组件以支持过期时间
- 添加 expirationTime 属性作为首选时间源 - 当 expirationTime 缺失时回退到 createTime + timeoutHours 方式 - 更新订单详情页和订单列表页组件以传递 expirationTime - 修改 usePaymentCountdown Hook 以支持新的参数结构 - 更新组件文档以反映新的 API 和使用方式 - 增强时间计算逻辑以处理无效时间情况
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# PaymentCountdown 支付倒计时组件
|
# PaymentCountdown 支付倒计时组件
|
||||||
|
|
||||||
基于订单创建时间的支付倒计时组件,支持静态显示和实时更新两种模式。
|
基于订单过期时间(`expirationTime`)的支付倒计时组件,支持静态显示和实时更新两种模式。
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
|
|
||||||
// 订单列表页 - 静态显示
|
// 订单列表页 - 静态显示
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
createTime={order.createTime}
|
expirationTime={order.expirationTime}
|
||||||
payStatus={order.payStatus}
|
payStatus={order.payStatus}
|
||||||
realTime={false}
|
realTime={false}
|
||||||
mode="badge"
|
mode="badge"
|
||||||
@@ -27,7 +27,7 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
|
|
||||||
// 订单详情页 - 实时更新
|
// 订单详情页 - 实时更新
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
createTime={order.createTime}
|
expirationTime={order.expirationTime}
|
||||||
payStatus={order.payStatus}
|
payStatus={order.payStatus}
|
||||||
realTime={true}
|
realTime={true}
|
||||||
showSeconds={true}
|
showSeconds={true}
|
||||||
@@ -43,7 +43,7 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
```tsx
|
```tsx
|
||||||
// 自定义超时时间(12小时)
|
// 自定义超时时间(12小时)
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
createTime={order.createTime}
|
expirationTime={order.expirationTime}
|
||||||
payStatus={order.payStatus}
|
payStatus={order.payStatus}
|
||||||
realTime={true}
|
realTime={true}
|
||||||
timeoutHours={12}
|
timeoutHours={12}
|
||||||
@@ -55,7 +55,7 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
|
|
||||||
// 纯文本模式
|
// 纯文本模式
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
createTime={order.createTime}
|
expirationTime={order.expirationTime}
|
||||||
payStatus={order.payStatus}
|
payStatus={order.payStatus}
|
||||||
realTime={false}
|
realTime={false}
|
||||||
mode="text"
|
mode="text"
|
||||||
@@ -67,6 +67,7 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
| 参数 | 类型 | 默认值 | 说明 |
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|------|------|--------|------|
|
|------|------|--------|------|
|
||||||
| createTime | string | - | 订单创建时间 |
|
| createTime | string | - | 订单创建时间 |
|
||||||
|
| expirationTime | string | - | 订单过期时间(推荐) |
|
||||||
| payStatus | boolean | false | 支付状态 |
|
| payStatus | boolean | false | 支付状态 |
|
||||||
| realTime | boolean | false | 是否实时更新 |
|
| realTime | boolean | false | 是否实时更新 |
|
||||||
| timeoutHours | number | 24 | 超时小时数 |
|
| timeoutHours | number | 24 | 超时小时数 |
|
||||||
@@ -102,12 +103,13 @@ import PaymentCountdown from '@/components/PaymentCountdown';
|
|||||||
import { usePaymentCountdown, formatCountdownText } from '@/hooks/usePaymentCountdown';
|
import { usePaymentCountdown, formatCountdownText } from '@/hooks/usePaymentCountdown';
|
||||||
|
|
||||||
const MyComponent = ({ order }) => {
|
const MyComponent = ({ order }) => {
|
||||||
const timeLeft = usePaymentCountdown(
|
const timeLeft = usePaymentCountdown({
|
||||||
order.createTime,
|
expirationTime: order.expirationTime,
|
||||||
order.payStatus,
|
createTime: order.createTime, // expirationTime 缺失时回退
|
||||||
true, // 实时更新
|
payStatus: order.payStatus,
|
||||||
24 // 24小时超时
|
realTime: true,
|
||||||
);
|
timeoutHours: 24
|
||||||
|
});
|
||||||
|
|
||||||
const countdownText = formatCountdownText(timeLeft, true);
|
const countdownText = formatCountdownText(timeLeft, true);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import './PaymentCountdown.scss';
|
|||||||
export interface PaymentCountdownProps {
|
export interface PaymentCountdownProps {
|
||||||
/** 订单创建时间 */
|
/** 订单创建时间 */
|
||||||
createTime?: string;
|
createTime?: string;
|
||||||
|
/** 订单过期时间(推荐直接传后端返回的 expirationTime) */
|
||||||
|
expirationTime?: string;
|
||||||
/** 支付状态 */
|
/** 支付状态 */
|
||||||
payStatus?: boolean;
|
payStatus?: boolean;
|
||||||
/** 是否实时更新(详情页用true,列表页用false) */
|
/** 是否实时更新(详情页用true,列表页用false) */
|
||||||
@@ -29,6 +31,7 @@ export interface PaymentCountdownProps {
|
|||||||
|
|
||||||
const PaymentCountdown: React.FC<PaymentCountdownProps> = ({
|
const PaymentCountdown: React.FC<PaymentCountdownProps> = ({
|
||||||
createTime,
|
createTime,
|
||||||
|
expirationTime,
|
||||||
payStatus = false,
|
payStatus = false,
|
||||||
realTime = false,
|
realTime = false,
|
||||||
timeoutHours = 24,
|
timeoutHours = 24,
|
||||||
@@ -37,10 +40,16 @@ const PaymentCountdown: React.FC<PaymentCountdownProps> = ({
|
|||||||
onExpired,
|
onExpired,
|
||||||
mode = 'badge'
|
mode = 'badge'
|
||||||
}) => {
|
}) => {
|
||||||
const timeLeft = usePaymentCountdown(createTime, payStatus, realTime, timeoutHours);
|
const timeLeft = usePaymentCountdown({
|
||||||
|
createTime,
|
||||||
|
expirationTime,
|
||||||
|
payStatus,
|
||||||
|
realTime,
|
||||||
|
timeoutHours
|
||||||
|
});
|
||||||
|
|
||||||
// 如果已支付或没有创建时间,不显示倒计时
|
// 如果已支付或没有可计算的截止时间,不显示倒计时
|
||||||
if (payStatus || !createTime) {
|
if (payStatus || (!expirationTime && !createTime)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,19 +13,30 @@ export interface CountdownTime {
|
|||||||
totalMinutes: number; // 总剩余分钟数
|
totalMinutes: number; // 总剩余分钟数
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UsePaymentCountdownParams {
|
||||||
|
/** 订单创建时间(用于兼容:当 expirationTime 缺失时按 createTime + timeoutHours 计算) */
|
||||||
|
createTime?: string;
|
||||||
|
/** 订单过期时间(推荐直接传后端返回的 expirationTime) */
|
||||||
|
expirationTime?: string;
|
||||||
|
/** 支付状态 */
|
||||||
|
payStatus?: boolean;
|
||||||
|
/** 是否实时更新(详情页用true,列表页用false) */
|
||||||
|
realTime?: boolean;
|
||||||
|
/** 超时小时数,默认24小时(仅在 expirationTime 缺失时生效) */
|
||||||
|
timeoutHours?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付倒计时Hook
|
* 支付倒计时Hook
|
||||||
* @param createTime 订单创建时间
|
* 优先使用 expirationTime;当 expirationTime 缺失时回退到 createTime + timeoutHours。
|
||||||
* @param payStatus 支付状态
|
|
||||||
* @param realTime 是否实时更新(详情页用true,列表页用false)
|
|
||||||
* @param timeoutHours 超时小时数,默认24小时
|
|
||||||
*/
|
*/
|
||||||
export const usePaymentCountdown = (
|
export const usePaymentCountdown = ({
|
||||||
createTime?: string,
|
createTime,
|
||||||
payStatus?: boolean,
|
expirationTime,
|
||||||
realTime: boolean = false,
|
payStatus,
|
||||||
timeoutHours: number = 24
|
realTime = false,
|
||||||
): CountdownTime => {
|
timeoutHours = 24
|
||||||
|
}: UsePaymentCountdownParams): CountdownTime => {
|
||||||
const [timeLeft, setTimeLeft] = useState<CountdownTime>({
|
const [timeLeft, setTimeLeft] = useState<CountdownTime>({
|
||||||
hours: 0,
|
hours: 0,
|
||||||
minutes: 0,
|
minutes: 0,
|
||||||
@@ -37,7 +48,7 @@ export const usePaymentCountdown = (
|
|||||||
// 计算剩余时间的函数
|
// 计算剩余时间的函数
|
||||||
const calculateTimeLeft = useMemo(() => {
|
const calculateTimeLeft = useMemo(() => {
|
||||||
return (): CountdownTime => {
|
return (): CountdownTime => {
|
||||||
if (!createTime || payStatus) {
|
if (payStatus || (!expirationTime && !createTime)) {
|
||||||
return {
|
return {
|
||||||
hours: 0,
|
hours: 0,
|
||||||
minutes: 0,
|
minutes: 0,
|
||||||
@@ -47,8 +58,27 @@ export const usePaymentCountdown = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const createTimeObj = dayjs(createTime);
|
// 优先使用后端过期时间;如果无法解析,再回退到 createTime + timeoutHours
|
||||||
const expireTime = createTimeObj.add(timeoutHours, 'hour');
|
const expireTimeFromExpiration = expirationTime ? dayjs(expirationTime) : null;
|
||||||
|
const expireTimeFromCreate =
|
||||||
|
createTime ? dayjs(createTime).add(timeoutHours, 'hour') : null;
|
||||||
|
const expireTime =
|
||||||
|
expireTimeFromExpiration?.isValid()
|
||||||
|
? expireTimeFromExpiration
|
||||||
|
: expireTimeFromCreate?.isValid()
|
||||||
|
? expireTimeFromCreate
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!expireTime) {
|
||||||
|
return {
|
||||||
|
hours: 0,
|
||||||
|
minutes: 0,
|
||||||
|
seconds: 0,
|
||||||
|
isExpired: true,
|
||||||
|
totalMinutes: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
const diff = expireTime.diff(now);
|
const diff = expireTime.diff(now);
|
||||||
|
|
||||||
@@ -76,10 +106,10 @@ export const usePaymentCountdown = (
|
|||||||
totalMinutes
|
totalMinutes
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}, [createTime, payStatus, timeoutHours]);
|
}, [createTime, expirationTime, payStatus, timeoutHours]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!createTime || payStatus) {
|
if (payStatus || (!expirationTime && !createTime)) {
|
||||||
setTimeLeft({
|
setTimeLeft({
|
||||||
hours: 0,
|
hours: 0,
|
||||||
minutes: 0,
|
minutes: 0,
|
||||||
@@ -111,7 +141,7 @@ export const usePaymentCountdown = (
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, [createTime, payStatus, realTime, calculateTimeLeft]);
|
}, [createTime, expirationTime, payStatus, realTime, calculateTimeLeft]);
|
||||||
|
|
||||||
return timeLeft;
|
return timeLeft;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ const OrderDetail = () => {
|
|||||||
{!order.payStatus && order.orderStatus !== 2 && (
|
{!order.payStatus && order.orderStatus !== 2 && (
|
||||||
<div className="order-detail-countdown flex justify-center p-4 border-b border-gray-50">
|
<div className="order-detail-countdown flex justify-center p-4 border-b border-gray-50">
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
|
expirationTime={order.expirationTime}
|
||||||
createTime={order.createTime}
|
createTime={order.createTime}
|
||||||
payStatus={order.payStatus}
|
payStatus={order.payStatus}
|
||||||
realTime={true}
|
realTime={true}
|
||||||
|
|||||||
@@ -733,6 +733,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
<View className={`${getOrderStatusColor(item)} font-medium`}>
|
<View className={`${getOrderStatusColor(item)} font-medium`}>
|
||||||
{!item.payStatus && item.orderStatus !== 2 ? (
|
{!item.payStatus && item.orderStatus !== 2 ? (
|
||||||
<PaymentCountdown
|
<PaymentCountdown
|
||||||
|
expirationTime={item.expirationTime}
|
||||||
createTime={item.createTime}
|
createTime={item.createTime}
|
||||||
payStatus={item.payStatus}
|
payStatus={item.payStatus}
|
||||||
realTime={false}
|
realTime={false}
|
||||||
|
|||||||
Reference in New Issue
Block a user