Files
web/src/app/admin/identify/page.tsx

186 lines
6.7 KiB
TypeScript
Raw Normal View History

'use client'
import {Button} from '@/components/ui/button'
import banner from './_assets/banner.webp'
import personal from './_assets/personal.webp'
import Image from 'next/image'
import Page from '@/components/page'
import {Dialog, DialogContent, DialogFooter, DialogTitle, DialogTrigger} from '@/components/ui/dialog'
import {Form, FormField} from '@/components/ui/form'
import {useForm} from 'react-hook-form'
import zod from 'zod'
import {zodResolver} from '@hookform/resolvers/zod'
import {Identify} from '@/actions/auth/identify'
import {toast} from 'sonner'
import {useContext, useEffect, useRef, useState} from 'react'
import * as qrcode from 'qrcode'
import {AuthContext} from '@/components/providers/AuthProvider'
export type IdentifyPageProps = {}
export default function IdentifyPage(props: IdentifyPageProps) {
// ======================
// 填写信息
// ======================
const schema = zod.object({
type: zod.enum([`personal`, `enterprise`], {errorMap: () => ({message: `请选择认证类型`})}).default('personal'),
name: zod.string().min(2, {message: `姓名至少2个字符`}),
iden_no: zod.string().length(18, {message: `身份证号码必须为18位`}),
})
type Schema = zod.infer<typeof schema>
const form = useForm<Schema>({
resolver: zodResolver(schema),
defaultValues: {
type: `personal`,
name: ``,
iden_no: ``,
},
})
const handler = form.handleSubmit(
async (value) => {
const resp = await Identify({
type: value.type === `personal` ? 1 : 2,
name: value.name,
iden_no: value.iden_no,
})
if (resp.success) {
form.reset()
if (!resp.data?.identified) {
setStep('scan')
setTarget(resp.data.target)
}
else {
toast.success('认证已完成')
}
}
else {
console.log(resp.message)
toast.error(`认证信息提交失败:请稍后重试`)
}
},
)
// ======================
// 扫码认证
// ======================
const [step, setStep] = useState<'form' | 'scan'>('form')
const [target, setTarget] = useState('')
const [openDialog, setOpenDialog] = useState(false)
const canvas = useRef<HTMLCanvasElement>(null)
useEffect(() => {
if (canvas.current && target) {
qrcode.toCanvas(canvas.current, target, {
width: 256,
margin: 0,
}, (error) => {
if (error) {
console.error(error)
toast.error(`二维码生成失败:请稍后重试`)
}
})
}
}, [target])
// ======================
// 用户数据
// ======================
const ctx = useContext(AuthContext)
console.log('render identify page')
// ======================
// render
// ======================
return (
<Page mode={`blank`}>
<div className={`flex-3/4 flex flex-col bg-white rounded-lg overflow-hidden gap-16`}>
{/* banner */}
<section className={`flex-none basis-40 relative 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-auto flex justify-center items-start`}>
{/* 个人认证 */}
<section className={`w-96 bg-gray-50 p-8 rounded-md flex flex-col gap-4 items-center`}>
<Image src={personal} alt={`个人认证`}/>
<div>
<h3 className={`text-center text-lg font-bold`}></h3>
<p className={`text-sm text-gray-600`}></p>
</div>
{ctx.profile?.id_token ? (
<div className={`flex flex-col gap-4`}>
<p className={`text-sm text-gray-600`}></p>
</div>
) : (
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
<DialogTrigger asChild>
<Button className={`w-full`}></Button>
</DialogTrigger>
<DialogContent>
<DialogTitle>
{step === 'form' ? `实名认证` : `扫码完成认证`}
</DialogTitle>
{step === 'form' && (
<Form form={form} handler={handler} className={`flex flex-col gap-4`}>
<FormField<Schema> name={`name`} label={`姓名`}>
{({id, field}) => (
<input
{...field}
id={id}
placeholder={`请输入姓名`}
className={`border rounded p-2 w-full`}
autoComplete={`name`}
/>
)}
</FormField>
<FormField<Schema> name={`iden_no`} label={`身份证号`}>
{({id, field}) => (
<input
{...field}
id={id}
placeholder={`请输入身份证号`}
className={`border rounded p-2 w-full`}
/>
)}
</FormField>
<DialogFooter>
<Button type={`submit`} className={`w-full mt-4`}></Button>
</DialogFooter>
</Form>
)}
{step === 'scan' && (
<div className={`flex flex-col gap-4 items-center`}>
<canvas ref={canvas} width={256} height={256}/>
<p className={`text-sm text-gray-600`}></p>
<Button onClick={async () => {
await ctx.refreshProfile()
setOpenDialog(false)
}}>
</Button>
</div>
)}
</DialogContent>
</Dialog>
)}
</section>
</div>
</div>
<section className={`flex-1/4 flex flex-col bg-white rounded-lg`}>
<h3></h3>
<p>使HTTP代理需完成实名认证</p>
</section>
</Page>
)
}