Files
web/src/components/send-msg.tsx

84 lines
2.1 KiB
TypeScript
Raw Normal View History

2025-12-11 14:10:52 +08:00
'use client'
import {useEffect, useRef, useState} from 'react'
import Cap from '@cap.js/widget'
import {sendSMS} from '@/actions/verify'
import {toast} from 'sonner'
import {Button} from '@/components/ui/button'
export default function SendMsg(props: {
phone: string
}) {
const [countdown, setCountdown] = useState(0)
const [progress, setProgress] = useState(0)
const cap = useRef(new Cap({apiEndpoint: '/'}))
cap.current.addEventListener('solve', (event) => {
console.log('captcha solve', event)
})
cap.current.addEventListener('error', (event) => {
console.error('captcha error', event)
})
cap.current.addEventListener('reset', (event) => {
console.log('captcha reset', event)
})
cap.current.addEventListener('progress', (event) => {
setProgress(event.detail.progress)
})
// 计时
useEffect(() => {
const interval = setInterval(() => {
if (countdown > 0) {
setCountdown(countdown - 1)
}
}, 1000)
return () => clearInterval(interval)
}, [countdown])
// 发送验证码
const sendCode = async () => {
try {
// 检查手机号
const valid = /^1\d{10}$/.test(props.phone)
if (!valid) {
throw new Error('请输入正确的手机号')
}
// 完成挑战
const result = await cap.current.solve()
if (!result.success || !cap.current.token) {
throw new Error('人机验证失败')
}
// 发送验证码
const resp = await sendSMS({
phone: props.phone,
captcha: cap.current.token,
})
if (!resp.success) {
throw new Error(`验证码发送失败: ${resp.message}`)
}
setCountdown(60)
toast.success('验证码已发送')
}
catch (e) {
toast.error('验证码发送失败', {
description: (e as Error).message,
})
}
}
return (
<Button
type="button"
theme="outline"
className="whitespace-nowrap h-10"
disabled={countdown > 0}
onClick={sendCode}
>
{cap.current.token ? '1' : '0'}
{countdown > 0 ? `${countdown}秒后重发` : '获取验证码'}
</Button>
)
}