时里院子市集
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.
 
 
 
 

163 lines
3.9 KiB

import { useState, useEffect, useMemo } from 'react';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
// 扩展dayjs支持duration
dayjs.extend(duration);
export interface CountdownTime {
hours: number;
minutes: number;
seconds: number;
isExpired: boolean;
totalMinutes: number; // 总剩余分钟数
}
/**
* 支付倒计时Hook
* @param createTime 订单创建时间
* @param payStatus 支付状态
* @param realTime 是否实时更新(详情页用true,列表页用false)
* @param timeoutHours 超时小时数,默认24小时
*/
export const usePaymentCountdown = (
createTime?: string,
payStatus?: boolean,
realTime: boolean = false,
timeoutHours: number = 24
): CountdownTime => {
const [timeLeft, setTimeLeft] = useState<CountdownTime>({
hours: 0,
minutes: 0,
seconds: 0,
isExpired: false,
totalMinutes: 0
});
// 计算剩余时间的函数
const calculateTimeLeft = useMemo(() => {
return (): CountdownTime => {
if (!createTime || payStatus) {
return {
hours: 0,
minutes: 0,
seconds: 0,
isExpired: false,
totalMinutes: 0
};
}
const createTimeObj = dayjs(createTime);
const expireTime = createTimeObj.add(timeoutHours, 'hour');
const now = dayjs();
const diff = expireTime.diff(now);
if (diff <= 0) {
return {
hours: 0,
minutes: 0,
seconds: 0,
isExpired: true,
totalMinutes: 0
};
}
const durationObj = dayjs.duration(diff);
const hours = Math.floor(durationObj.asHours());
const minutes = durationObj.minutes();
const seconds = durationObj.seconds();
const totalMinutes = Math.floor(durationObj.asMinutes());
return {
hours,
minutes,
seconds,
isExpired: false,
totalMinutes
};
};
}, [createTime, payStatus, timeoutHours]);
useEffect(() => {
if (!createTime || payStatus) {
setTimeLeft({
hours: 0,
minutes: 0,
seconds: 0,
isExpired: false,
totalMinutes: 0
});
return;
}
// 立即计算一次
const initialTime = calculateTimeLeft();
setTimeLeft(initialTime);
// 如果不需要实时更新,直接返回
if (!realTime) {
return;
}
// 如果需要实时更新,设置定时器
const timer = setInterval(() => {
const newTimeLeft = calculateTimeLeft();
setTimeLeft(newTimeLeft);
// 如果已过期,清除定时器
if (newTimeLeft.isExpired) {
clearInterval(timer);
}
}, 1000);
return () => clearInterval(timer);
}, [createTime, payStatus, realTime, calculateTimeLeft]);
return timeLeft;
};
/**
* 格式化倒计时文本
* @param timeLeft 倒计时时间对象
* @param showSeconds 是否显示秒数
*/
export const formatCountdownText = (
timeLeft: CountdownTime,
showSeconds: boolean = false
): string => {
if (timeLeft.isExpired) {
return '已过期';
}
if (timeLeft.hours > 0) {
if (showSeconds) {
return `${timeLeft.hours}小时${timeLeft.minutes}${timeLeft.seconds}`;
} else {
return `${timeLeft.hours}小时${timeLeft.minutes}分钟`;
}
} else if (timeLeft.minutes > 0) {
if (showSeconds) {
return `${timeLeft.minutes}${timeLeft.seconds}`;
} else {
return `${timeLeft.minutes}分钟`;
}
} else {
return `${timeLeft.seconds}`;
}
};
/**
* 判断是否为紧急状态(剩余时间少于1小时)
*/
export const isUrgentCountdown = (timeLeft: CountdownTime): boolean => {
return !timeLeft.isExpired && timeLeft.totalMinutes < 60;
};
/**
* 判断是否为非常紧急状态(剩余时间少于10分钟)
*/
export const isCriticalCountdown = (timeLeft: CountdownTime): boolean => {
return !timeLeft.isExpired && timeLeft.totalMinutes < 10;
};
export default usePaymentCountdown;