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.
64 lines
1.9 KiB
64 lines
1.9 KiB
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { Clock } from "@phosphor-icons/react";
|
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
|
|
|
interface CountdownTimerProps {
|
|
validUntil: string;
|
|
onExpire?: () => void;
|
|
}
|
|
|
|
export function CountdownTimer({ validUntil, onExpire }: CountdownTimerProps) {
|
|
const [remainMs, setRemainMs] = useState(
|
|
() => new Date(validUntil).getTime() - Date.now(),
|
|
);
|
|
const [showExpiredDialog, setShowExpiredDialog] = useState(false);
|
|
const expiredCalled = useRef(false);
|
|
|
|
useEffect(() => {
|
|
expiredCalled.current = false;
|
|
setShowExpiredDialog(false);
|
|
const id = setInterval(() => {
|
|
const left = new Date(validUntil).getTime() - Date.now();
|
|
setRemainMs(left);
|
|
if (left <= 0 && !expiredCalled.current) {
|
|
expiredCalled.current = true;
|
|
clearInterval(id);
|
|
setShowExpiredDialog(true);
|
|
onExpire?.();
|
|
}
|
|
}, 500);
|
|
return () => clearInterval(id);
|
|
}, [validUntil, onExpire]);
|
|
|
|
const safe = Math.max(0, remainMs);
|
|
const mm = String(Math.floor(safe / 60_000)).padStart(2, "0");
|
|
const ss = String(Math.floor((safe % 60_000) / 1_000)).padStart(2, "0");
|
|
const isWarning = safe < 30_000 && safe > 0;
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={`inline-flex items-center gap-1.5 text-sm font-medium ${
|
|
isWarning ? "text-warning" : "text-text-secondary"
|
|
}`}
|
|
>
|
|
<Clock size={16} weight={isWarning ? "fill" : "regular"} />
|
|
<span className="num">
|
|
报价有效期 {mm}:{ss}
|
|
</span>
|
|
</div>
|
|
<ConfirmDialog
|
|
open={showExpiredDialog}
|
|
title="报价已过期"
|
|
message="当前报价已超过 3 分钟有效期,请重新询价获取最新价格。"
|
|
confirmLabel="重新询价"
|
|
cancelLabel="关闭"
|
|
onConfirm={() => setShowExpiredDialog(false)}
|
|
onCancel={() => setShowExpiredDialog(false)}
|
|
/>
|
|
</>
|
|
);
|
|
}
|