| 1 | /** |
| 2 | * useKeepTimeCountdown - 任务保留时间倒计时 Hook |
| 3 | * 根据 keepTime(秒) 和 acceptedAt(起算时间) 计算剩余秒数并每秒递减 |
| 4 | */ |
| 5 | |
| 6 | import { useEffect, useState } from 'react' |
| 7 | |
| 8 | function calcRemaining(keepTime: number, acceptedAt?: string): number { |
| 9 | if (keepTime <= 0 || !acceptedAt) |
| 10 | return -1 // -1 表示不限时 |
| 11 | const deadline = new Date(acceptedAt).getTime() + keepTime * 1000 |
| 12 | const remaining = Math.floor((deadline - Date.now()) / 1000) |
| 13 | return Math.max(remaining, 0) |
| 14 | } |
| 15 | |
| 16 | export function formatCountdown(totalSeconds: number): string { |
| 17 | if (totalSeconds < 0) |
| 18 | return '' |
| 19 | const hours = Math.floor(totalSeconds / 3600) |
| 20 | const minutes = Math.floor((totalSeconds % 3600) / 60) |
| 21 | const seconds = totalSeconds % 60 |
| 22 | |
| 23 | const pad = (n: number) => String(n).padStart(2, '0') |
| 24 | return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}` |
| 25 | } |
| 26 | |
| 27 | export function useKeepTimeCountdown(keepTime: number, acceptedAt?: string) { |
| 28 | const [remaining, setRemaining] = useState(() => calcRemaining(keepTime, acceptedAt)) |
| 29 | |
| 30 | useEffect(() => { |
| 31 | const initial = calcRemaining(keepTime, acceptedAt) |
| 32 | setRemaining(initial) |
| 33 | |
| 34 | if (initial <= 0) |
| 35 | return |
| 36 | |
| 37 | const timer = setInterval(() => { |
| 38 | setRemaining((prev) => { |
| 39 | if (prev <= 1) { |
| 40 | clearInterval(timer) |
| 41 | return 0 |
| 42 | } |
| 43 | return prev - 1 |
| 44 | }) |
| 45 | }, 1000) |
| 46 | |
| 47 | return () => clearInterval(timer) |
| 48 | }, [keepTime, acceptedAt]) |
| 49 | |
| 50 | return { |
| 51 | remaining, // 剩余秒数,-1 表示不限时,0 表示已过期 |
| 52 | isExpired: remaining === 0, |
| 53 | isUnlimited: remaining < 0, |
| 54 | formatted: formatCountdown(remaining), |
| 55 | } |
| 56 | } |
| 57 |