完成个人中心页面功能,公共组件部分调整
This commit is contained in:
@@ -1,282 +0,0 @@
|
||||
'use client'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog'
|
||||
import {Button} from '@/components/ui/button'
|
||||
import {Form, FormField} from '@/components/ui/form'
|
||||
import {useForm} from 'react-hook-form'
|
||||
import zod from 'zod'
|
||||
import FormOption from '@/components/composites/purchase/_client/option'
|
||||
import {RadioGroup} from '@/components/ui/radio-group'
|
||||
import Image from 'next/image'
|
||||
import {zodResolver} from '@hookform/resolvers/zod'
|
||||
import {toast} from 'sonner'
|
||||
import wechat from '@/components/composites/purchase/_assets/wechat.svg'
|
||||
import alipay from '@/components/composites/purchase/_assets/alipay.svg'
|
||||
import {useContext, useRef, useState} from 'react'
|
||||
import {Loader} from 'lucide-react'
|
||||
import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user'
|
||||
import * as qrcode from 'qrcode'
|
||||
import {StoreContext, useProfileStore} from '@/components/providers/StoreProvider'
|
||||
|
||||
const schema = zod.object({
|
||||
method: zod.enum(['alipay', 'wechat']),
|
||||
amount: zod.number().min(1, '充值金额必须大于 0'),
|
||||
})
|
||||
|
||||
type Schema = zod.infer<typeof schema>
|
||||
|
||||
export type RechargeModelProps = {}
|
||||
|
||||
export default function RechargeModal(props: RechargeModelProps) {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const form = useForm<Schema>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
method: 'alipay',
|
||||
amount: 50,
|
||||
},
|
||||
})
|
||||
|
||||
const method = form.watch('method')
|
||||
const amount = form.watch('amount')
|
||||
|
||||
const canvas = useRef<HTMLCanvasElement>(null)
|
||||
const [payInfo, setPayInfo] = useState<{
|
||||
trade_no: string
|
||||
pay_url: string
|
||||
}>()
|
||||
|
||||
const refreshProfile = useProfileStore(store => store.refreshProfile)
|
||||
|
||||
const createRecharge = async (data: Schema) => {
|
||||
try {
|
||||
switch (data.method) {
|
||||
case 'alipay':
|
||||
const aliRes = await RechargeByAlipay({
|
||||
amount: data.amount,
|
||||
})
|
||||
if (aliRes.success) {
|
||||
setStep(1)
|
||||
setPayInfo(aliRes.data)
|
||||
}
|
||||
else {
|
||||
toast.error(`充值失败`, {
|
||||
description: aliRes.message,
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'wechat':
|
||||
const weRes = await RechargeByWechat({
|
||||
amount: data.amount,
|
||||
})
|
||||
if (weRes.success) {
|
||||
setStep(1)
|
||||
setPayInfo(weRes.data)
|
||||
await qrcode.toCanvas(canvas.current, weRes.data.pay_url)
|
||||
}
|
||||
else {
|
||||
toast.error(`充值失败`, {
|
||||
description: weRes.message,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
toast.error(`充值失败`, {
|
||||
description: (e as Error).message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const confirmRecharge = async () => {
|
||||
if (!payInfo) {
|
||||
toast.error(`充值失败`, {
|
||||
description: `订单信息不存在`,
|
||||
})
|
||||
return
|
||||
}
|
||||
try {
|
||||
switch (method) {
|
||||
case 'alipay':
|
||||
const aliRes = await RechargeByAlipayConfirm({
|
||||
trade_no: payInfo.trade_no,
|
||||
})
|
||||
if (!aliRes.success) {
|
||||
throw new Error(aliRes.message)
|
||||
}
|
||||
break
|
||||
case 'wechat':
|
||||
const weRes = await RechargeByWechatConfirm({
|
||||
trade_no: payInfo.trade_no,
|
||||
})
|
||||
if (!weRes.success) {
|
||||
throw new Error(weRes.message)
|
||||
}
|
||||
break
|
||||
}
|
||||
toast.success(`充值成功`)
|
||||
closeDialog()
|
||||
await refreshProfile()
|
||||
}
|
||||
catch (e) {
|
||||
toast.error(`充值失败`, {
|
||||
description: (e as Error).message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
setOpen(false)
|
||||
setPayInfo(undefined)
|
||||
setStep(0)
|
||||
}
|
||||
|
||||
const [step, setStep] = useState(0)
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button theme={`accent`} type={`button`} className={`px-4 h-8`}>去充值</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogTitle className={`flex flex-col gap-2`}>
|
||||
充值中心
|
||||
</DialogTitle>
|
||||
|
||||
{step === 0 && (
|
||||
<Form form={form} onSubmit={createRecharge} className={`flex flex-col gap-8`}>
|
||||
|
||||
{/* 充值额度 */}
|
||||
<FormField<Schema> name={`amount`} label={`充值额度`} className={`flex flex-col gap-4`}>
|
||||
{({id, field}) => (
|
||||
<RadioGroup
|
||||
id={id}
|
||||
defaultValue={String(field.value)}
|
||||
onValueChange={v => field.onChange(Number(v))}
|
||||
className={`flex flex-col gap-2`}>
|
||||
|
||||
<div className={`flex items-center gap-2`}>
|
||||
<FormOption
|
||||
id={`${id}-20`} value={`20`} label={`20元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
<FormOption
|
||||
id={`${id}-50`} value={`50`} label={`50元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
<FormOption
|
||||
id={`${id}-100`} value={`100`} label={`100元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`flex items-center gap-2`}>
|
||||
<FormOption
|
||||
id={`${id}-200`} value={`200`} label={`200元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
<FormOption
|
||||
id={`${id}-500`} value={`500`} label={`500元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
<FormOption
|
||||
id={`${id}-1000`} value={`1000`} label={`1000元`}
|
||||
compare={String(field.value)}
|
||||
className={`flex-1`}
|
||||
/>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
)}
|
||||
</FormField>
|
||||
|
||||
{/* 支付方式 */}
|
||||
<FormField name={`method`} label={`支付方式`} className={`flex flex-col gap-4`}>
|
||||
{({id, field}) => (
|
||||
<RadioGroup
|
||||
id={id}
|
||||
defaultValue={field.value}
|
||||
onValueChange={field.onChange}
|
||||
className={`flex gap-2`}>
|
||||
<FormOption
|
||||
id={`${id}-alipay`} value={`alipay`}
|
||||
compare={field.value}
|
||||
className={`flex-1 flex-row justify-center items-center`}>
|
||||
<Image src={alipay} alt={`支付宝 logo`} className={`w-6 h-6`}/>
|
||||
<span>支付宝</span>
|
||||
</FormOption>
|
||||
<FormOption
|
||||
id={`${id}-wechat`} value={`wechat`}
|
||||
compare={field.value}
|
||||
className={`flex-1 flex-row justify-center items-center`}>
|
||||
<Image src={wechat} alt={`微信 logo`} className={`w-6 h-6`}/>
|
||||
<span>微信</span>
|
||||
</FormOption>
|
||||
</RadioGroup>
|
||||
)}
|
||||
</FormField>
|
||||
|
||||
<DialogFooter className={`!flex !flex-row !justify-center`}>
|
||||
<Button className={`px-8 h-12 text-lg`}>立即支付</Button>
|
||||
</DialogFooter>
|
||||
</Form>
|
||||
)}
|
||||
{step == 1 && <>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="bg-gray-100 w-52 h-52 flex items-center justify-center">
|
||||
{payInfo ?
|
||||
method === 'alipay'
|
||||
? <iframe
|
||||
src={payInfo.pay_url}
|
||||
className="w-full h-full"
|
||||
title="支付二维码"
|
||||
/>
|
||||
: <canvas ref={canvas} className="w-full h-full"/>
|
||||
: (
|
||||
<Loader size={40} className={`animate-spin text-weak`}/>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 text-center">
|
||||
请使用{method === 'alipay' ? '支付宝' : '微信'}扫码支付
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center space-y-1">
|
||||
<p className="font-medium">支付金额: <span className="text-accent">{amount}元</span></p>
|
||||
<p className="text-xs text-gray-500">订单号: {payInfo?.trade_no || '创建订单中...'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className={`!flex !flex-row !justify-center`}>
|
||||
<Button
|
||||
className={`px-8 text-lg`}
|
||||
onClick={confirmRecharge}
|
||||
>
|
||||
已完成支付
|
||||
</Button>
|
||||
<Button
|
||||
theme={`outline`}
|
||||
className={`px-8 text-lg`}
|
||||
onClick={closeDialog}
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</>}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import alipay from '@/components/composites/purchase/_assets/alipay.svg'
|
||||
import wechat from '@/components/composites/purchase/_assets/wechat.svg'
|
||||
import balance from '@/components/composites/purchase/_assets/balance.svg'
|
||||
import {useProfileStore} from '@/components/providers/StoreProvider'
|
||||
import RechargeModal from '@/components/composites/purchase/_client/recharge'
|
||||
import RechargeModal from '@/components/composites/recharge'
|
||||
import Pay from '@/components/composites/purchase/_client/pay'
|
||||
import {buttonVariants} from '@/components/ui/button'
|
||||
import Link from 'next/link'
|
||||
|
||||
Reference in New Issue
Block a user