2025-04-19 12:56:32 +08:00
|
|
|
|
'use client'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import {useEffect, useRef, useState} from 'react'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
import Page from '@/components/page'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'
|
|
|
|
|
|
import {Form, FormField} from '@/components/ui/form'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
import {Button} from '@/components/ui/button'
|
|
|
|
|
|
import {useForm} from 'react-hook-form'
|
|
|
|
|
|
import {zodResolver} from '@hookform/resolvers/zod'
|
|
|
|
|
|
import * as z from 'zod'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import {useProfileStore} from '@/components/providers/StoreProvider'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
import {toast} from 'sonner'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import {CheckCircle, QrCodeIcon} from 'lucide-react'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
import * as qrcode from 'qrcode'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import Image from 'next/image'
|
|
|
|
|
|
import banner from '@/app/admin/identify/_assets/banner.webp'
|
|
|
|
|
|
import {Input} from '@/components/ui/input'
|
|
|
|
|
|
import {merge} from '@/lib/utils'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
import {User} from '@/lib/models'
|
2025-04-29 18:47:36 +08:00
|
|
|
|
import {update, updatePassword} from '@/actions/user'
|
|
|
|
|
|
import {
|
|
|
|
|
|
Dialog, DialogContent,
|
|
|
|
|
|
DialogFooter,
|
|
|
|
|
|
DialogHeader,
|
|
|
|
|
|
DialogTitle,
|
|
|
|
|
|
DialogTrigger,
|
|
|
|
|
|
} from '@/components/ui/dialog'
|
|
|
|
|
|
import {sendSMS} from '@/actions/verify'
|
|
|
|
|
|
import RechargeModal from '@/components/composites/recharge'
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
|
|
|
|
|
export type ProfilePageProps = {}
|
|
|
|
|
|
|
|
|
|
|
|
export default function ProfilePage(props: ProfilePageProps) {
|
2025-04-29 18:47:36 +08:00
|
|
|
|
|
2025-04-23 19:00:53 +08:00
|
|
|
|
const profile = useProfileStore(store => store.profile)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
// ======================
|
|
|
|
|
|
// render
|
|
|
|
|
|
// ======================
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (!profile) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Page>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<div className={`flex flex-col gap-4`}>
|
|
|
|
|
|
<h3 className={`text-lg font-bold`}>加载中...</h3>
|
|
|
|
|
|
<p className={`text-sm text-gray-600`}>请稍等片刻</p>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</Page>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<Page className="flex-row items-start">
|
|
|
|
|
|
<div className={`flex-3/4 flex flex-col gap-4`}>
|
|
|
|
|
|
{/* banner */}
|
|
|
|
|
|
<section className={`flex-none basis-40 relative rounded-lg overflow-hidden flex flex-col gap-4 pl-8 justify-center`}>
|
|
|
|
|
|
<Image src={banner} alt={`背景图`} aria-hidden className={`absolute inset-0 w-full h-full object-cover`}/>
|
|
|
|
|
|
<h3 className={`text-lg font-bold z-10 relative`}>蓝狐HTTP邀请您参与【先测后买】服务</h3>
|
|
|
|
|
|
<p className={`text-sm text-gray-600 z-10 relative`}>为了保障您的账户安全,请先完成实名认证,即可获取福利套餐测试资格</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 块信息 */}
|
|
|
|
|
|
<div className={`flex-none basis-40 flex gap-4`}>
|
|
|
|
|
|
|
|
|
|
|
|
<Card className={`flex-1`}>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
<CardHeader>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<CardTitle className={`font-normal`}>帐号余额(元)</CardTitle>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</CardHeader>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<CardContent className={`flex-auto flex justify-between items-center px-8`}>
|
|
|
|
|
|
<p className={`text-xl`}>{profile?.balance}</p>
|
|
|
|
|
|
<RechargeModal/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<Card className={`flex-1`}>
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardTitle className={`font-normal`}>实名认证</CardTitle>
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
<CardContent className={`flex-auto flex justify-between items-center px-8`}>
|
|
|
|
|
|
{!profile?.id_token
|
|
|
|
|
|
? <>
|
|
|
|
|
|
<p className={`text-sm`}>为了保障您的账户安全和正常使用服务,请您尽快完成实名认证</p>
|
|
|
|
|
|
<Button theme={`outline`} className={`mx-16 w-24`}>去认证</Button>
|
|
|
|
|
|
</>
|
|
|
|
|
|
: <>
|
|
|
|
|
|
<p className={`flex flex-col gap-1`}>
|
|
|
|
|
|
<span>{profile.name}</span>
|
|
|
|
|
|
<span className={`text-sm`}>{profile.id_no}</span>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</p>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<p className={`flex gap-1 items-center`}>
|
|
|
|
|
|
<CheckCircle className={`text-done`} size={18}/>
|
|
|
|
|
|
<span>已认证</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</>}
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<div className={`flex-auto shrink-0 basis-80 rounded-lg bg-white p-4 flex flex-col gap-8`}>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 安全信息 */}
|
|
|
|
|
|
<div className={`flex flex-col gap-4`}>
|
|
|
|
|
|
<h3 className={`font-normal`}>安全信息</h3>
|
|
|
|
|
|
<div className={`flex gap-4 items-center`}>
|
|
|
|
|
|
<p>{profile.phone}</p>
|
|
|
|
|
|
<PasswordForm profile={profile}/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 基本信息 */}
|
|
|
|
|
|
<BasicForm profile={profile}/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 侧边栏:客服经理信息 */}
|
|
|
|
|
|
<Aftersale profile={profile}/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
</Page>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
function Aftersale(props: {
|
2025-04-19 12:56:32 +08:00
|
|
|
|
profile: User
|
|
|
|
|
|
}) {
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const admin = props.profile.admin_id
|
|
|
|
|
|
const canvasRef = useRef(null)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (admin && canvasRef.current) {
|
|
|
|
|
|
qrcode.toCanvas(canvasRef.current, String(admin), {
|
|
|
|
|
|
width: 180,
|
|
|
|
|
|
margin: 0,
|
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
|
console.error(err)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [admin])
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
|
|
|
|
|
return (
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<Card className="flex-none basis-80">
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardTitle>
|
|
|
|
|
|
<QrCodeIcon size={18}/> 关于售后
|
|
|
|
|
|
</CardTitle>
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
<CardContent className={`flex flex-col gap-8`}>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={`flex flex-col gap-4`}>
|
|
|
|
|
|
<p className={`text-weak text-sm`}>
|
|
|
|
|
|
1.全国100万+动态IP代理资源免费测试,先测后买让您安心使用。
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</p>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<p className={`text-weak text-sm`}>
|
|
|
|
|
|
2.注册即享新人福利,专业的客户经理,多维度为您提供在线代理相关答疑
|
|
|
|
|
|
</p>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<p className={`text-weak text-sm`}>
|
|
|
|
|
|
3.1V1专属售后答疑,技术团队7*24小时在线支持提供专属解决方案
|
|
|
|
|
|
</p>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<div className={`flex flex-col gap-4 items-center`}>
|
|
|
|
|
|
<p>您的专属客服经理</p>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<canvas ref={canvasRef} width="180" height="180" className={`mx-auto bg-muted`}/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p className="text-xs text-weak">
|
|
|
|
|
|
扫描上方二维码添加客服经理微信<br/>获取更多帮助与支持
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
function BasicForm(props: {
|
2025-04-19 12:56:32 +08:00
|
|
|
|
profile: User
|
|
|
|
|
|
}) {
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const schema = z.object({
|
|
|
|
|
|
username: z.string(),
|
|
|
|
|
|
email: z.string().email('请输入正确的邮箱'),
|
|
|
|
|
|
contact_qq: z.string(),
|
|
|
|
|
|
contact_wechat: z.string(),
|
|
|
|
|
|
})
|
|
|
|
|
|
type Schema = z.infer<typeof schema>
|
|
|
|
|
|
const form = useForm<Schema>({
|
|
|
|
|
|
resolver: zodResolver(schema),
|
|
|
|
|
|
defaultValues: {
|
|
|
|
|
|
username: props.profile.username || '',
|
|
|
|
|
|
email: props.profile.email || '',
|
|
|
|
|
|
contact_qq: props.profile.contact_qq || '',
|
|
|
|
|
|
contact_wechat: props.profile.contact_wechat || '',
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
const handler = form.handleSubmit(async value => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const resp = await update(value)
|
|
|
|
|
|
if (!resp.success) {
|
|
|
|
|
|
throw new Error(resp.message)
|
|
|
|
|
|
}
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
await refreshProfile()
|
|
|
|
|
|
toast.success(`保存成功`)
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (e) {
|
|
|
|
|
|
console.error(e)
|
|
|
|
|
|
toast.error(`保存失败`, {
|
|
|
|
|
|
description: e instanceof Error ? e.message : String(e),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const refreshProfile = useProfileStore(store => store.refreshProfile)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
|
|
|
|
|
return (
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<div className={`flex flex-col gap-4`}>
|
|
|
|
|
|
<h3 className={`font-normal`}>基本信息</h3>
|
|
|
|
|
|
<Form
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
handler={handler}
|
|
|
|
|
|
className={merge(
|
|
|
|
|
|
`grid grid-cols-2 gap-4 items-start`,
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<FormField<Schema>
|
|
|
|
|
|
name={`username`}
|
|
|
|
|
|
label={<span className={`w-full flex justify-end`}>用户名</span>}
|
|
|
|
|
|
className={`grid grid-cols-[48px_1fr] grid-rows-[auto_auto] gap-x-4`}
|
|
|
|
|
|
classNames={{
|
|
|
|
|
|
message: `col-start-2`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入用户名`} className={`w-52`}/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
<FormField<Schema>
|
|
|
|
|
|
name={`email`}
|
|
|
|
|
|
label={<span className={`w-full flex justify-end`}>邮箱</span>}
|
|
|
|
|
|
className={`grid grid-cols-[48px_1fr] grid-rows-[auto_auto] gap-x-4`}
|
|
|
|
|
|
classNames={{
|
|
|
|
|
|
message: `col-start-2`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入邮箱`} className={`w-52`}/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
<FormField<Schema>
|
|
|
|
|
|
name={`contact_qq`}
|
|
|
|
|
|
label={<span className={`w-full flex justify-end`}>QQ</span>}
|
|
|
|
|
|
className={`grid grid-cols-[48px_1fr] grid-rows-[auto_auto] gap-x-4`}
|
|
|
|
|
|
classNames={{
|
|
|
|
|
|
message: `col-start-2`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入QQ号`} className={`w-52`}/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
<FormField<Schema>
|
|
|
|
|
|
name={`contact_wechat`}
|
|
|
|
|
|
label={<span className={`w-full flex justify-end`}>微信</span>}
|
|
|
|
|
|
className={`grid grid-cols-[48px_1fr] grid-rows-[auto_auto] gap-x-4`}
|
|
|
|
|
|
classNames={{
|
|
|
|
|
|
message: `col-start-2`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入微信号`} className={`w-52`}/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
<div className={`flex justify-end gap-4 col-span-2 justify-self-stretch`}>
|
|
|
|
|
|
<Button theme={`outline`} type={`button`} onClick={() => form.reset({
|
|
|
|
|
|
username: props.profile.username || '',
|
|
|
|
|
|
email: props.profile.email || '',
|
|
|
|
|
|
contact_qq: props.profile.contact_qq || '',
|
|
|
|
|
|
contact_wechat: props.profile.contact_wechat || '',
|
|
|
|
|
|
})}>撤销</Button>
|
|
|
|
|
|
<Button>保存</Button>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</div>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
</Form>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
function PasswordForm(props: {
|
|
|
|
|
|
profile: User
|
2025-04-19 12:56:32 +08:00
|
|
|
|
}) {
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
// ======================
|
|
|
|
|
|
// open
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
|
|
|
|
|
|
const [open, setOpen] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
// form
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
|
|
|
|
|
|
const schema = z.object({
|
|
|
|
|
|
phone: z.string().regex(/^1\d{10}$/, `请输入正确的手机号`),
|
|
|
|
|
|
captcha: z.string().nonempty('请输入验证码'),
|
|
|
|
|
|
code: z.string().regex(/^\d{6}$/, `请输入正确的验证码`),
|
|
|
|
|
|
password: z.string().min(6, `密码至少6位`),
|
|
|
|
|
|
confirm_password: z.string(),
|
|
|
|
|
|
}).refine(data => data.password === data.confirm_password, {
|
|
|
|
|
|
message: '两次输入的密码不一致',
|
|
|
|
|
|
path: ['confirm_password'],
|
2025-04-19 12:56:32 +08:00
|
|
|
|
})
|
2025-04-29 18:47:36 +08:00
|
|
|
|
type Schema = z.infer<typeof schema>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const form = useForm<Schema>({
|
|
|
|
|
|
resolver: zodResolver(schema),
|
2025-04-19 12:56:32 +08:00
|
|
|
|
defaultValues: {
|
2025-04-29 18:47:36 +08:00
|
|
|
|
phone: '',
|
|
|
|
|
|
captcha: '',
|
|
|
|
|
|
code: '',
|
|
|
|
|
|
password: '',
|
|
|
|
|
|
confirm_password: '',
|
2025-04-19 12:56:32 +08:00
|
|
|
|
},
|
|
|
|
|
|
})
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const handler = form.handleSubmit(async value => {
|
2025-04-19 12:56:32 +08:00
|
|
|
|
try {
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const resp = await updatePassword({
|
|
|
|
|
|
phone: value.phone,
|
|
|
|
|
|
code: value.code,
|
|
|
|
|
|
password: value.password,
|
|
|
|
|
|
})
|
|
|
|
|
|
if (!resp.success) {
|
|
|
|
|
|
throw new Error(resp.message)
|
|
|
|
|
|
}
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
toast.success(`保存成功`)
|
|
|
|
|
|
setOpen(false)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
}
|
2025-04-29 18:47:36 +08:00
|
|
|
|
catch (e) {
|
|
|
|
|
|
console.error(e)
|
|
|
|
|
|
toast.error(`保存失败`, {
|
|
|
|
|
|
description: e instanceof Error ? e.message : String(e),
|
2025-04-19 12:56:32 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-04-29 18:47:36 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
// phone code
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
|
|
|
|
|
|
const [captchaUrl, setCaptchaUrl] = useState(`/captcha?t=${new Date().getTime()}`)
|
|
|
|
|
|
const [captchaWait, setCaptchaWait] = useState(0)
|
|
|
|
|
|
const interval = useRef<NodeJS.Timeout>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const refreshCaptcha = () => {
|
|
|
|
|
|
setCaptchaUrl(`/captcha?t=${new Date().getTime()}`)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const sendVerifier = async () => {
|
|
|
|
|
|
const result = await form.trigger(['phone', 'captcha'])
|
|
|
|
|
|
if (!result) {
|
2025-04-19 12:56:32 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
const {phone, captcha} = form.getValues()
|
|
|
|
|
|
const resp = await sendSMS({phone, captcha})
|
|
|
|
|
|
if (!resp.success) {
|
|
|
|
|
|
toast.error(resp.message)
|
|
|
|
|
|
refreshCaptcha()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
setCaptchaWait(60)
|
|
|
|
|
|
interval.current = setInterval(() => {
|
|
|
|
|
|
setCaptchaWait(wait => {
|
|
|
|
|
|
if (wait <= 1) {
|
|
|
|
|
|
clearInterval(interval.current!)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
return 0
|
|
|
|
|
|
}
|
2025-04-29 18:47:36 +08:00
|
|
|
|
return wait - 1
|
2025-04-19 12:56:32 +08:00
|
|
|
|
})
|
|
|
|
|
|
}, 1000)
|
2025-04-29 18:47:36 +08:00
|
|
|
|
|
|
|
|
|
|
toast.success(`验证码已发送,请注意查收`)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
// ======================
|
|
|
|
|
|
// render
|
|
|
|
|
|
// ======================
|
|
|
|
|
|
|
2025-04-19 12:56:32 +08:00
|
|
|
|
return (
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
|
<Button theme={`outline`} className={`w-24 h-9`}>修改密码</Button>
|
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
|
<DialogContent>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
<DialogHeader>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<DialogTitle>修改密码</DialogTitle>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</DialogHeader>
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<Form form={form} handler={handler} className={`flex flex-col gap-4 mt-4`}>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 手机号 */}
|
|
|
|
|
|
<FormField<Schema> name={`phone`} label={`手机号`} className={`flex-auto`}>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入手机号`} autoComplete={`tel-national`}/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<FormField<Schema> name={`captcha`} label={`验证码`}>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<div className={`flex gap-4`}>
|
|
|
|
|
|
<Input {...field} placeholder={`请输入验证码`} autoComplete={`one-time-code`}/>
|
|
|
|
|
|
<Button className={`p-0 bg-transparent`} onClick={refreshCaptcha} type={`button`}>
|
|
|
|
|
|
<img src={captchaUrl} alt={`验证码`} className={`h-10`}/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 短信令牌 */}
|
|
|
|
|
|
<FormField<Schema> name={`code`} label={`短信令牌`} className={`flex-auto`}>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<div className={`flex gap-4`}>
|
|
|
|
|
|
<Input {...field} placeholder={`请输入验证码`} autoComplete={`one-time-code`}/>
|
|
|
|
|
|
<Button theme={`outline`} type={`button`} className={`w-36`} onClick={() => sendVerifier()}>
|
|
|
|
|
|
{captchaWait > 0
|
|
|
|
|
|
? `重新发送(${captchaWait})`
|
|
|
|
|
|
: `获取验证码`
|
|
|
|
|
|
}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 新密码 */}
|
|
|
|
|
|
<FormField<Schema> name={`password`} label={`新密码`} className={`flex-auto`}>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请输入新密码`} type={`password`} autoComplete={`new-password`}/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
{/* 确认密码 */}
|
|
|
|
|
|
<FormField<Schema> name={`confirm_password`} label={`确认密码`} className={`flex-auto`}>
|
|
|
|
|
|
{({field}) => (
|
|
|
|
|
|
<Input {...field} placeholder={`请再次输入新密码`} type={`password`} autoComplete={`new-password`}/>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
<DialogFooter>
|
|
|
|
|
|
<Button theme={`outline`} type={`button`} onClick={() => {
|
|
|
|
|
|
setOpen(false)
|
|
|
|
|
|
form.reset()
|
|
|
|
|
|
}}>关闭</Button>
|
|
|
|
|
|
<Button onClick={async e => {
|
|
|
|
|
|
const result = await handler(e)
|
2025-04-19 12:56:32 +08:00
|
|
|
|
|
2025-04-29 18:47:36 +08:00
|
|
|
|
}}>保存</Button>
|
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
</Dialog>
|
2025-04-19 12:56:32 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|