修复表单响应性问题 & 更新套餐管理接口字段
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
### 禁止直接依赖 form
|
||||||
|
|
||||||
|
`\[(.*,)?form(,.*)?\]`
|
||||||
|
|
||||||
业务场景页面优化实现
|
业务场景页面优化实现
|
||||||
|
|
||||||
业务定制页面优化实现
|
业务定制页面优化实现
|
||||||
|
|||||||
@@ -1,8 +1,23 @@
|
|||||||
'use server'
|
'use server'
|
||||||
|
import {callByDevice, callByUser} from '@/actions/base'
|
||||||
import {callByUser} from '@/actions/base'
|
|
||||||
import {Resource} from '@/lib/models'
|
import {Resource} from '@/lib/models'
|
||||||
import {ExtraReq, PageRecord} from '@/lib/api'
|
import {PageRecord} from '@/lib/api'
|
||||||
|
|
||||||
|
export type CreateResourceReq = {
|
||||||
|
type: number
|
||||||
|
short?: {
|
||||||
|
live: number
|
||||||
|
mode: number
|
||||||
|
quota: number
|
||||||
|
expire?: number
|
||||||
|
}
|
||||||
|
long?: {
|
||||||
|
live: number
|
||||||
|
mode: number
|
||||||
|
quota: number
|
||||||
|
expire?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function listResourceShort(props: {
|
export async function listResourceShort(props: {
|
||||||
page: number
|
page: number
|
||||||
@@ -34,45 +49,14 @@ export async function allResource() {
|
|||||||
return callByUser<Resource<1 | 2>[]>('/api/resource/all')
|
return callByUser<Resource<1 | 2>[]>('/api/resource/all')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createResource(props: {
|
export async function createResource(props: CreateResourceReq) {
|
||||||
type: number
|
|
||||||
short?: {
|
|
||||||
live: number
|
|
||||||
mode: number
|
|
||||||
quota: number
|
|
||||||
expire_at: number
|
|
||||||
daily_limit: number
|
|
||||||
}
|
|
||||||
long?: {
|
|
||||||
live: number
|
|
||||||
mode: number
|
|
||||||
quota: number
|
|
||||||
expire_at: number
|
|
||||||
daily_limit: number
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return await callByUser('/api/resource/create', props)
|
return await callByUser('/api/resource/create', props)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareResource(props: {
|
export async function prepareResource(props: {
|
||||||
type: number
|
|
||||||
short?: {
|
|
||||||
live: number
|
|
||||||
mode: number
|
|
||||||
quota: number
|
|
||||||
expire_at: number
|
|
||||||
daily_limit: number
|
|
||||||
}
|
|
||||||
long?: {
|
|
||||||
live: number
|
|
||||||
mode: number
|
|
||||||
quota: number
|
|
||||||
expire_at: number
|
|
||||||
daily_limit: number
|
|
||||||
}
|
|
||||||
payment_method: number
|
payment_method: number
|
||||||
payment_platform: number
|
payment_platform: number
|
||||||
}) {
|
} & CreateResourceReq) {
|
||||||
return await callByUser<{
|
return await callByUser<{
|
||||||
trade_no: string
|
trade_no: string
|
||||||
pay_url: string
|
pay_url: string
|
||||||
@@ -101,3 +85,7 @@ export async function payClose(props: {
|
|||||||
}) {
|
}) {
|
||||||
return callByUser('/api/trade/cancel', props)
|
return callByUser('/api/trade/cancel', props)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPrice(props: CreateResourceReq) {
|
||||||
|
return callByDevice<{price: string}>('/api/resource/price', props)
|
||||||
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ function Tab(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SendMsgByUsername() {
|
function SendMsgByUsername() {
|
||||||
const form = useFormContext<LoginSchema>()
|
const {control} = useFormContext<LoginSchema>()
|
||||||
const phone = form.watch('username')
|
const phone = useWatch({control, name: 'username'})
|
||||||
return <SendMsg phone={phone}/>
|
return <SendMsg phone={phone}/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTr
|
|||||||
import {Button} from '@/components/ui/button'
|
import {Button} from '@/components/ui/button'
|
||||||
import {Form, FormField} from '@/components/ui/form'
|
import {Form, FormField} from '@/components/ui/form'
|
||||||
import {Input} from '@/components/ui/input'
|
import {Input} from '@/components/ui/input'
|
||||||
import {useForm, useFormContext} from 'react-hook-form'
|
import {useForm, useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {zodResolver} from '@hookform/resolvers/zod'
|
import {zodResolver} from '@hookform/resolvers/zod'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import {toast} from 'sonner'
|
import {toast} from 'sonner'
|
||||||
@@ -154,7 +154,7 @@ export function ChangePasswordDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SendMsgByPhone() {
|
function SendMsgByPhone() {
|
||||||
const form = useFormContext<Schema>()
|
const {control} = useFormContext<Schema>()
|
||||||
const phone = form.watch('phone')
|
const phone = useWatch({control, name: 'phone'})
|
||||||
return <SendMsg phone={phone}/>
|
return <SendMsg phone={phone}/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {RadioGroup, RadioGroupItem} from '@/components/ui/radio-group'
|
|||||||
import {Input} from '@/components/ui/input'
|
import {Input} from '@/components/ui/input'
|
||||||
import {Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue} from '@/components/ui/select'
|
import {Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue} from '@/components/ui/select'
|
||||||
import {Button} from '@/components/ui/button'
|
import {Button} from '@/components/ui/button'
|
||||||
import {useForm, useFormContext} from 'react-hook-form'
|
import {useForm, useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {Alert, AlertTitle} from '@/components/ui/alert'
|
import {Alert, AlertTitle} from '@/components/ui/alert'
|
||||||
import {ArrowRight, Box, CircleAlert, CopyIcon, ExternalLinkIcon, LinkIcon, Loader, Plus, Timer} from 'lucide-react'
|
import {ArrowRight, Box, CircleAlert, CopyIcon, ExternalLinkIcon, LinkIcon, Loader, Plus, Timer} from 'lucide-react'
|
||||||
import {memo, ReactNode, useEffect, useRef, useState} from 'react'
|
import {memo, ReactNode, useEffect, useRef, useState} from 'react'
|
||||||
@@ -487,10 +487,10 @@ function SelectResource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SelectRegion() {
|
function SelectRegion() {
|
||||||
const form = useFormContext<Schema>()
|
const {control, setValue} = useFormContext<Schema>()
|
||||||
const regionType = form.watch('regionType')
|
const regionType = useWatch({control, name: 'regionType'})
|
||||||
const prov = form.watch('prov')
|
const prov = useWatch({control, name: 'prov'})
|
||||||
const city = form.watch('city')
|
const city = useWatch({control, name: 'city'})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 md:max-w-[calc(160px*2+1rem)]">
|
<div className="flex flex-col gap-4 md:max-w-[calc(160px*2+1rem)]">
|
||||||
@@ -500,8 +500,8 @@ function SelectRegion() {
|
|||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
field.onChange(e)
|
field.onChange(e)
|
||||||
if (e === 'unlimited') {
|
if (e === 'unlimited') {
|
||||||
form.setValue('prov', '')
|
setValue('prov', '')
|
||||||
form.setValue('city', '')
|
setValue('city', '')
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
defaultValue={field.value}
|
defaultValue={field.value}
|
||||||
@@ -525,8 +525,8 @@ function SelectRegion() {
|
|||||||
options={cities.options}
|
options={cities.options}
|
||||||
value={[prov || '', city || '']}
|
value={[prov || '', city || '']}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
form.setValue('prov', value[0])
|
setValue('prov', value[0])
|
||||||
form.setValue('city', value[1])
|
setValue('city', value[1])
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -536,7 +536,7 @@ function SelectRegion() {
|
|||||||
|
|
||||||
function ApplyLink() {
|
function ApplyLink() {
|
||||||
const form = useFormContext<Schema>()
|
const form = useFormContext<Schema>()
|
||||||
const values = form.watch()
|
useWatch()
|
||||||
|
|
||||||
// let type: 'open' | 'copy' = 'open'
|
// let type: 'open' | 'copy' = 'open'
|
||||||
const type = useRef<'open' | 'copy'>('open')
|
const type = useRef<'open' | 'copy'>('open')
|
||||||
@@ -613,7 +613,7 @@ function ApplyLink() {
|
|||||||
|
|
||||||
{/* 展示链接地址 */}
|
{/* 展示链接地址 */}
|
||||||
<div className="bg-secondary p-4 rounded-md break-all">
|
<div className="bg-secondary p-4 rounded-md break-all">
|
||||||
{link(values)}
|
{link(form.getValues())}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 操作 */}
|
{/* 操作 */}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import FormOption from '@/components/composites/purchase/option'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import check from '../_assets/check.svg'
|
import check from '../_assets/check.svg'
|
||||||
import {Schema} from '@/components/composites/purchase/long/form'
|
import {Schema} from '@/components/composites/purchase/long/form'
|
||||||
import {useFormContext} from 'react-hook-form'
|
import {useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {Card} from '@/components/ui/card'
|
import {Card} from '@/components/ui/card'
|
||||||
|
|
||||||
export default function Center() {
|
export default function Center() {
|
||||||
const form = useFormContext<Schema>()
|
const form = useFormContext<Schema>()
|
||||||
const type = form.watch('type')
|
const type = useWatch({name: 'type'})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
|
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
|
||||||
|
|||||||
@@ -14,20 +14,18 @@ import Pay from '@/components/composites/purchase/pay'
|
|||||||
import {buttonVariants} from '@/components/ui/button'
|
import {buttonVariants} from '@/components/ui/button'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import {merge} from '@/lib/utils'
|
import {merge} from '@/lib/utils'
|
||||||
import {useFormContext} from 'react-hook-form'
|
import {useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {Schema} from '@/components/composites/purchase/long/form'
|
import {Schema} from '@/components/composites/purchase/long/form'
|
||||||
import {Card} from '@/components/ui/card'
|
import {Card} from '@/components/ui/card'
|
||||||
|
|
||||||
export default function Right() {
|
export default function Right() {
|
||||||
const profile = useProfileStore(store => store.profile)
|
const {control} = useFormContext<Schema>()
|
||||||
const form = useFormContext<Schema>()
|
const method = useWatch({control, name: 'pay_type'})
|
||||||
|
const mode = useWatch({control, name: 'type'})
|
||||||
const method = form.watch('pay_type')
|
const live = useWatch({control, name: 'live'})
|
||||||
const mode = form.watch('type')
|
const quota = useWatch({control, name: 'quota'})
|
||||||
const live = form.watch('live')
|
const expire = useWatch({control, name: 'expire'})
|
||||||
const quota = form.watch('quota')
|
const dailyLimit = useWatch({control, name: 'daily_limit'})
|
||||||
const expire = form.watch('expire')
|
|
||||||
const dailyLimit = form.watch('daily_limit')
|
|
||||||
|
|
||||||
const price = useMemo(() => {
|
const price = useMemo(() => {
|
||||||
const base = {
|
const base = {
|
||||||
@@ -173,9 +171,8 @@ function BalanceOrLogin(props: {
|
|||||||
long: {
|
long: {
|
||||||
mode: Number(props.mode),
|
mode: Number(props.mode),
|
||||||
live: Number(props.live),
|
live: Number(props.live),
|
||||||
daily_limit: props.dailyLimit,
|
expire: Number(props.expire),
|
||||||
expire_at: Number(props.expire),
|
quota: props.mode === '1' ? props.dailyLimit : props.quota,
|
||||||
quota: props.quota,
|
|
||||||
},
|
},
|
||||||
}}/>
|
}}/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {useProfileStore} from '@/components/stores/profile'
|
|||||||
import {Alert, AlertTitle} from '@/components/ui/alert'
|
import {Alert, AlertTitle} from '@/components/ui/alert'
|
||||||
import {toast} from 'sonner'
|
import {toast} from 'sonner'
|
||||||
import {useRouter} from 'next/navigation'
|
import {useRouter} from 'next/navigation'
|
||||||
import {completeResource, createResource, prepareResource} from '@/actions/resource'
|
import {completeResource, createResource, CreateResourceReq, prepareResource} from '@/actions/resource'
|
||||||
import {
|
import {
|
||||||
TradeMethod,
|
TradeMethod,
|
||||||
TradeMethodDecoration,
|
TradeMethodDecoration,
|
||||||
@@ -19,7 +19,7 @@ import {usePlatformType} from '@/lib/hooks'
|
|||||||
|
|
||||||
export type PayProps = {
|
export type PayProps = {
|
||||||
amount: string
|
amount: string
|
||||||
resource: Parameters<typeof createResource>[0]
|
resource: CreateResourceReq
|
||||||
} & ({
|
} & ({
|
||||||
method: 'alipay' | 'wechat'
|
method: 'alipay' | 'wechat'
|
||||||
} | {
|
} | {
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import {Minus, Plus} from 'lucide-react'
|
|||||||
import FormOption from '@/components/composites/purchase/option'
|
import FormOption from '@/components/composites/purchase/option'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import check from '../_assets/check.svg'
|
import check from '../_assets/check.svg'
|
||||||
import {useFormContext} from 'react-hook-form'
|
import {useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {Schema} from '@/components/composites/purchase/short/form'
|
import {Schema} from '@/components/composites/purchase/short/form'
|
||||||
import {Card} from '@/components/ui/card'
|
import {Card} from '@/components/ui/card'
|
||||||
|
|
||||||
export default function Center() {
|
export default function Center() {
|
||||||
const form = useFormContext<Schema>()
|
const form = useFormContext<Schema>()
|
||||||
const type = form.watch('type')
|
const type = useWatch({name: 'type'})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
|
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
|
||||||
@@ -82,8 +82,8 @@ export default function Center() {
|
|||||||
theme="outline"
|
theme="outline"
|
||||||
type="button"
|
type="button"
|
||||||
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
||||||
onClick={() => form.setValue('quota', Math.max(10_000, Number(field.value) - 5_000))}
|
onClick={() => form.setValue('quota', Math.max(10000, Number(field.value) - 5000))}
|
||||||
disabled={Number(field.value) === 10_000}>
|
disabled={Number(field.value) === 10000}>
|
||||||
<Minus/>
|
<Minus/>
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
@@ -91,14 +91,13 @@ export default function Center() {
|
|||||||
id={id}
|
id={id}
|
||||||
type="number"
|
type="number"
|
||||||
className="w-40 h-10 border border-gray-200 rounded-sm text-center"
|
className="w-40 h-10 border border-gray-200 rounded-sm text-center"
|
||||||
min={10_000}
|
min={10000}
|
||||||
step={5_000}
|
step={5000}/>
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
type="button"
|
type="button"
|
||||||
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
||||||
onClick={() => form.setValue('quota', Number(field.value) + 5_000)}>
|
onClick={() => form.setValue('quota', Number(field.value) + 5000)}>
|
||||||
<Plus/>
|
<Plus/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,18 +14,17 @@ import {buttonVariants} from '@/components/ui/button'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import {merge} from '@/lib/utils'
|
import {merge} from '@/lib/utils'
|
||||||
import Pay from '@/components/composites/purchase/pay'
|
import Pay from '@/components/composites/purchase/pay'
|
||||||
import {useFormContext} from 'react-hook-form'
|
import {useFormContext, useWatch} from 'react-hook-form'
|
||||||
import {Card} from '@/components/ui/card'
|
import {Card} from '@/components/ui/card'
|
||||||
|
|
||||||
export default function Right() {
|
export default function Right() {
|
||||||
const form = useFormContext<Schema>()
|
const {control} = useFormContext<Schema>()
|
||||||
|
const method = useWatch({control, name: 'pay_type'})
|
||||||
const method = form.watch('pay_type')
|
const live = useWatch({control, name: 'live'})
|
||||||
const live = form.watch('live')
|
const mode = useWatch({control, name: 'type'})
|
||||||
const mode = form.watch('type')
|
const dailyLimit = useWatch({control, name: 'daily_limit'})
|
||||||
const dailyLimit = form.watch('daily_limit')
|
const expire = useWatch({control, name: 'expire'})
|
||||||
const expire = form.watch('expire')
|
const quota = useWatch({control, name: 'quota'})
|
||||||
const quota = form.watch('quota')
|
|
||||||
|
|
||||||
const price = useMemo(() => {
|
const price = useMemo(() => {
|
||||||
const base = live === '180' ? 150 : Number(live)
|
const base = live === '180' ? 150 : Number(live)
|
||||||
@@ -165,9 +164,8 @@ function BalanceOrLogin(props: {
|
|||||||
short: {
|
short: {
|
||||||
mode: Number(props.mode),
|
mode: Number(props.mode),
|
||||||
live: Number(props.live),
|
live: Number(props.live),
|
||||||
quota: props.quota,
|
expire: Number(props.expire),
|
||||||
expire_at: Number(props.expire),
|
quota: props.mode === '1' ? props.dailyLimit : props.quota,
|
||||||
daily_limit: props.dailyLimit,
|
|
||||||
},
|
},
|
||||||
}}/>
|
}}/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -17,20 +17,13 @@ import React, {ComponentProps, createContext, ReactNode, useContext, useId} from
|
|||||||
|
|
||||||
type FormProps<T extends FieldValues> = {
|
type FormProps<T extends FieldValues> = {
|
||||||
form: UseFormReturn<T>
|
form: UseFormReturn<T>
|
||||||
onSubmit?: SubmitHandler<T>
|
|
||||||
onError?: SubmitErrorHandler<T>
|
|
||||||
handler?: (e?: React.BaseSyntheticEvent) => Promise<void>
|
handler?: (e?: React.BaseSyntheticEvent) => Promise<void>
|
||||||
} & Omit<ComponentProps<'form'>, 'onSubmit' | 'onError'>
|
} & Omit<ComponentProps<'form'>, 'onSubmit' | 'onError'>
|
||||||
|
|
||||||
function Form<T extends FieldValues>(rawProps: FormProps<T>) {
|
function Form<T extends FieldValues>(rawProps: FormProps<T>) {
|
||||||
const {children, onSubmit, onError, handler, ...props} = rawProps
|
const {children, handler, ...props} = rawProps
|
||||||
const form = props.form
|
const form = props.form
|
||||||
|
|
||||||
const handle = handler || form.handleSubmit(
|
|
||||||
onSubmit || ((_) => {}),
|
|
||||||
onError,
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -38,7 +31,7 @@ function Form<T extends FieldValues>(rawProps: FormProps<T>) {
|
|||||||
onSubmit={async (event) => {
|
onSubmit={async (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
await handle(event)
|
await handler?.(event)
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user