更新资源列表接口,调整相关数据结构,优化页面布局和样式
This commit is contained in:
@@ -210,9 +210,6 @@ export default function Center() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 左右的边框 */}
|
||||
<div className={`absolute inset-0 my-8 border-l border-r border-gray-200 pointer-events-none`}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
'use client'
|
||||
import {createContext, useContext} from 'react'
|
||||
import {createContext} from 'react'
|
||||
import {useForm, UseFormReturn} from 'react-hook-form'
|
||||
import Center from '@/components/composites/purchase/_client/center'
|
||||
import Right from '@/components/composites/purchase/_client/right'
|
||||
import Left from '@/components/composites/purchase/_client/left'
|
||||
import {Form} from '@/components/ui/form'
|
||||
import * as z from 'zod'
|
||||
import {zodResolver} from '@hookform/resolvers/zod'
|
||||
import {createResourceByBalance} from '@/actions/resource'
|
||||
import {toast} from 'sonner'
|
||||
import {useRouter} from 'next/navigation'
|
||||
import {StoreContext} from '@/components/providers/StoreProvider'
|
||||
|
||||
// 定义表单验证架构
|
||||
const schema = z.object({
|
||||
@@ -51,7 +46,6 @@ export default function PurchaseForm(props: PurchaseFormProps) {
|
||||
<section role={`tabpanel`} className={`bg-white rounded-lg`}>
|
||||
<Form form={form} className={`flex flex-row`}>
|
||||
<PurchaseFormContext.Provider value={{form}}>
|
||||
<Left/>
|
||||
<Center/>
|
||||
<Right/>
|
||||
</PurchaseFormContext.Provider>
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
'use client'
|
||||
import {useState} from 'react'
|
||||
import Image from 'next/image'
|
||||
import banner from '@/components/composites/purchase/_assets/banner.webp'
|
||||
|
||||
export type LeftProps = {
|
||||
}
|
||||
|
||||
export default function Left(props: LeftProps) {
|
||||
return (
|
||||
<div className="flex-none basis-56 p-8 flex flex-col gap-4">
|
||||
<Image src={banner} alt={`banner`} className={`w-full`}/>
|
||||
<h3 className={`text-lg`}>包量套餐</h3>
|
||||
<ul className={`flex flex-col gap-3`}>
|
||||
<Combo name={`3分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`5分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`10分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`15分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`30分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
</ul>
|
||||
<div className={`border-b border-gray-200`}></div>
|
||||
<h3 className={`text-lg`}>包时套餐</h3>
|
||||
<ul className={`flex flex-col gap-3`}>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>7天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>9折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>30天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>8折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>90天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>7折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>180天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>6折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>360天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>5折</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Combo(props: {
|
||||
name: string
|
||||
level?: {
|
||||
number: number
|
||||
discount: number
|
||||
}[]
|
||||
}) {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<li>
|
||||
<p className={`flex justify-between items-center`}>
|
||||
<span>{props.name}</span>
|
||||
<button
|
||||
className={`text-gray-500 text-sm`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
{open ? '收起' : '展开'}
|
||||
</button>
|
||||
</p>
|
||||
{props.level && (
|
||||
<ul className={[
|
||||
`flex flex-col gap-3 overflow-hidden`,
|
||||
`transition-[opacity,padding,max-height] transition-discrete duration-200 ease-in-out`,
|
||||
open
|
||||
? 'delay-[0s, 0s] opacity-100 py-3 max-h-80'
|
||||
: 'delay-[0s, 0.2s] opacity-0 p-0 max-h-0',
|
||||
].join(' ')}>
|
||||
{props.level.map((item, index) => (
|
||||
<li key={index} className={`flex flex-row justify-between items-center`}>
|
||||
<span className={`text-gray-500 text-sm`}>{item.number}</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>赠送 {item.discount} %</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
27
src/components/composites/purchase/_client/nav.tsx
Normal file
27
src/components/composites/purchase/_client/nav.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
'use client'
|
||||
import {ReactNode, useState} from 'react'
|
||||
|
||||
export type NavProps = {
|
||||
}
|
||||
|
||||
export default function Nav(props: NavProps) {
|
||||
|
||||
const [type, setType] = useState()
|
||||
|
||||
return (
|
||||
<ul role={`tablist`} className={`flex justify-center items-stretch bg-white rounded-lg`}>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>短效动态套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>长效静态套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>固定套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>定制套餐</button>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -32,8 +32,8 @@ export type PayProps = {
|
||||
|
||||
export default function Pay(props: PayProps) {
|
||||
|
||||
const profile = useProfileStore(store=>store.profile)
|
||||
const refreshProfile = useProfileStore(store=>store.refreshProfile)
|
||||
const profile = useProfileStore(store => store.profile)
|
||||
const refreshProfile = useProfileStore(store => store.refreshProfile)
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [payInfo, setPayInfo] = useState<CreateResourceResp | undefined>()
|
||||
@@ -185,7 +185,7 @@ export default function Pay(props: PayProps) {
|
||||
<div className="bg-gray-100 size-50 flex items-center justify-center">
|
||||
{payInfo ? (
|
||||
props.method === 'alipay'
|
||||
? <iframe src={payInfo.pay_url} className="w-full h-full" />
|
||||
? <iframe src={payInfo.pay_url} className="w-full h-full"/>
|
||||
: <canvas ref={canvas} className="w-full h-full"/>
|
||||
) : (
|
||||
<Loader size={40} className={`animate-spin text-weak`}/>
|
||||
|
||||
@@ -13,6 +13,7 @@ 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'
|
||||
import {merge} from '@/lib/utils'
|
||||
|
||||
export type RightProps = {}
|
||||
|
||||
@@ -48,7 +49,10 @@ export default function Right(props: RightProps) {
|
||||
}, [watchDailyLimit, watchExpire, watchLive, watchQuota, watchType])
|
||||
|
||||
return (
|
||||
<div className={`flex-none basis-80 p-6 flex flex-col gap-6`}>
|
||||
<div className={merge(
|
||||
`flex-none basis-80 p-6 flex flex-col gap-6 relative`,
|
||||
`after:absolute after:inset-0 after:my-6 after:border-l after:border-gray-200 after:select-none after:pointer-events-none`,
|
||||
)}>
|
||||
<h3>订单详情</h3>
|
||||
<ul className={`flex flex-col gap-3`}>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import PurchaseForm from '@/components/composites/purchase/_client/form'
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
|
||||
import {ReactNode} from 'react'
|
||||
import {merge} from '@/lib/utils'
|
||||
|
||||
export type PurchaseProps = {}
|
||||
|
||||
@@ -6,22 +9,35 @@ export default async function Purchase(props: PurchaseProps) {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{/*<ul role={`tablist`} className={`flex justify-center items-stretch bg-white rounded-lg`}>*/}
|
||||
{/* <li role={`tab`}>*/}
|
||||
{/* <button className={`h-14 px-8 text-lg`}>短效动态套餐</button>*/}
|
||||
{/* </li>*/}
|
||||
{/* <li role={`tab`}>*/}
|
||||
{/* <button className={`h-14 px-8 text-lg`}>长效静态套餐</button>*/}
|
||||
{/* </li>*/}
|
||||
{/* <li role={`tab`}>*/}
|
||||
{/* <button className={`h-14 px-8 text-lg`}>固定套餐</button>*/}
|
||||
{/* </li>*/}
|
||||
{/* <li role={`tab`}>*/}
|
||||
{/* <button className={`h-14 px-8 text-lg`}>定制套餐</button>*/}
|
||||
{/* </li>*/}
|
||||
{/*</ul>*/}
|
||||
<PurchaseForm/>
|
||||
<Tabs defaultValue={`short`} className={`gap-4`}>
|
||||
<TabsList className={`w-full p-2 bg-white rounded-lg justify-center`}>
|
||||
<Tab value={`short`}>短效动态</Tab>
|
||||
<Tab value={`long`}>长效静态</Tab>
|
||||
<Tab value={`fixed`}>固定套餐</Tab>
|
||||
<Tab value={`custom`}>定制套餐</Tab>
|
||||
</TabsList>
|
||||
<TabsContent value={`short`}>
|
||||
<PurchaseForm/>
|
||||
</TabsContent>
|
||||
<TabsContent value={`long`}>
|
||||
<PurchaseForm/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Tab(props: {
|
||||
value: string
|
||||
children: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<TabsTrigger className={merge(
|
||||
`w-36 h-12 text-base font-normal flex-none`,
|
||||
`data-[state=active]:text-primary data-[state=active]:bg-primary-muted`,
|
||||
)} value={props.value}>
|
||||
{props.children}
|
||||
</TabsTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user