diff --git a/src/actions/announcement.ts b/src/actions/announcement.ts deleted file mode 100644 index a8460b4..0000000 --- a/src/actions/announcement.ts +++ /dev/null @@ -1,19 +0,0 @@ -'use server' - -import {PageRecord} from '@/lib/api' -import {Announcement} from '@/lib/models' -import {callByUser} from './base' - -export async function listAnnouncements(props: { - page: number - size: number - title?: string - type?: number - status?: number - create_after?: Date - create_before?: Date - update_after?: Date - update_before?: Date -}) { - return await callByUser>('/api/announcement/list', props) -} diff --git a/src/actions/dashboard.ts b/src/actions/dashboard.ts new file mode 100644 index 0000000..5a53c4a --- /dev/null +++ b/src/actions/dashboard.ts @@ -0,0 +1,43 @@ +'use server' + +import {callByUser} from './base' + +export async function listInitialization(props: { + page: number + size: number + title?: string + type?: number + status?: number + create_after?: Date + create_before?: Date + update_after?: Date + update_before?: Date + short_term_package?: number +}) { + return await callByUser<{ + short_term: string + short_term_monthly: string + long_term: string + long_term_monthly: string + list: { + id: number + title: string + created_at: Date + }[] + }>('/api/announcement/list', props) +} + +type listAccountReq = { + resource_no: number + create_after: Date + create_before: Date +} + +type listAccountResp = { + time: Date + count: number +}[] + +export async function listAccount(props: listAccountReq) { + return await callByUser('/api/account/list', props) +} diff --git a/src/app/admin/(dashboard)/_client/chart.tsx b/src/app/admin/(dashboard)/_client/chart.tsx index 96bd70d..5e0b441 100644 --- a/src/app/admin/(dashboard)/_client/chart.tsx +++ b/src/app/admin/(dashboard)/_client/chart.tsx @@ -1,35 +1,33 @@ -'use client' - -import { ChartConfig, ChartContainer } from "@/components/ui/chart" -import { Area, AreaChart, Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from "recharts" -import { addDays, format } from "date-fns" - -const data = Array(100).fill(0).map((_, i) => { - let time = new Date() - time = addDays(time, i) - return { - time: format(time, `MM/dd`), - count: Math.floor(Math.random() * 100) + 1, - } -}) +import {ChartConfig, ChartContainer} from '@/components/ui/chart' +import {Area, AreaChart, Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis} from 'recharts' +import {addDays, format} from 'date-fns' +import {listAccount} from '@/actions/dashboard' +import {ExtraReq, ExtraResp} from '@/lib/api' const config = { count: { label: `套餐使用量`, color: `var(--color-primary)`, - } + }, } satisfies ChartConfig -export default function DashboardChart() { +type DashboardChartProps = { + data: ExtraResp +} + +async function DashboardChart(props: DashboardChartProps) { return ( - - + + - + + - + + ) -} \ No newline at end of file +} +export default DashboardChart diff --git a/src/app/admin/(dashboard)/_client/charts.tsx b/src/app/admin/(dashboard)/_client/charts.tsx index 4b36cbb..bd26810 100644 --- a/src/app/admin/(dashboard)/_client/charts.tsx +++ b/src/app/admin/(dashboard)/_client/charts.tsx @@ -1,121 +1,155 @@ 'use client' -import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs" -import DashboardChart from "./chart" +import {Tabs, TabsList, TabsTrigger, TabsContent} from '@/components/ui/tabs' +import DashboardChart from './chart' import Image from 'next/image' import soon from '../_assets/coming-soon.svg' -import DatePicker from "@/components/date-picker" -import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card" -import { Form, FormField } from "@/components/ui/form" -import { Input } from "@/components/ui/input" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" +import DatePicker from '@/components/date-picker' +import {Card, CardHeader, CardTitle, CardContent} from '@/components/ui/card' +import {Form, FormField} from '@/components/ui/form' +import {Input} from '@/components/ui/input' +import {zodResolver} from '@hookform/resolvers/zod' +import {useForm} from 'react-hook-form' import zod from 'zod' import {merge} from '@/lib/utils' import mask from '../_assets/Mask group.webp' import {Button} from '@/components/ui/button' - - - - - - - +import {useStatus} from '@/lib/states' +import {useState} from 'react' +import {useSearchParams} from 'next/navigation' +import {log} from 'console' +import {listAccount} from '@/actions/dashboard' +import {ExtraReq, ExtraResp} from '@/lib/api' +import {toast} from 'sonner' +import {addDays, format} from 'date-fns' export default function Charts() { - + const dateStr = '2025-03-05' + const dateStrA = '2024-03-05' + const date = new Date(dateStr) + const dateA = new Date(dateStrA) + // const [submittedData, setSubmittedData] = useState>() + const [submittedData, setSubmittedData] = useState>([ + {time: new Date(), count: 80}, + {time: date, count: 100}, + {time: dateA, count: 50}, + // {time: `2023-10-03`, count: 80}, + // {time: `2023-10-04`, count: 200}, + // {time: `2023-10-05`, count: 150}, + ]) const data = [ - { time: `2023-10-01`, count: 100 }, - { time: `2023-10-02`, count: 50 }, - { time: `2023-10-03`, count: 80 }, - { time: `2023-10-04`, count: 200 }, - { time: `2023-10-05`, count: 150 }, + {time: `2023-10-01`, count: 100}, + {time: `2023-10-02`, count: 50}, + {time: `2023-10-03`, count: 80}, + {time: `2023-10-04`, count: 200}, + {time: `2023-10-05`, count: 150}, ] - const formSchema = zod.object({ - name: zod.string(), - age: zod.string(), - }) + const formSchema = zod.object({ + resource_no: zod.number().min(11, '请输入正确的套餐编号').max(11, '请输入正确的套餐编号').optional(), + create_after: zod.date().optional(), + create_before: zod.date().optional(), + }) type FormValues = zod.infer const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { - name: '', - age: '', }, }) + const handler = form.handleSubmit( + async (value) => { + const res = { + resource_no: value.resource_no ?? 0, + create_after: value.create_after ?? new Date(), + create_before: value.create_before ?? new Date(), + } + const resp = await listAccount(res) + if (!resp.success) { + toast.error('接口请求失败:' + resp.message) + return + } + resp.data.map((_, i) => { + let time = new Date() + time = addDays(time, i) + return { + time: format(time, `MM/dd`), + count: Math.floor(Math.random() * 100) + 1, + } + }) + setSubmittedData(resp.data) + }, + ) return ( - - - + + + - + {/* {`Mask */} - 动态 IP 套餐 - 静态 IP 套餐 + 动态 IP 套餐 + + 静态 IP 套餐 -
+ className={merge(`grid grid-cols-3 gap-4 items-start`)} + handler={handler} + form={form} > - 套餐编号} + 套餐编号} className={`grid grid-cols-[70px_1fr] grid-rows-[auto_auto] `} classNames={{ message: `col-start-2`, }} > - {({ field }) => ( - + {({field}) => ( + )}
- 时间范围筛选} + 时间范围筛选} className={`grid grid-cols-[100px_1fr] `} classNames={{ message: `col-start-2`, }} > - {({ field }) => ( + {({field}) => ( )} - - - - {({ field }) => ( + - + + {({field}) => ( )} - -
+ - - + + {submittedData && } - - {`coming + + coming soon

敬请期待

) -} \ No newline at end of file +} diff --git a/src/app/admin/(dashboard)/_client/pins.tsx b/src/app/admin/(dashboard)/_client/pins.tsx deleted file mode 100644 index d890735..0000000 --- a/src/app/admin/(dashboard)/_client/pins.tsx +++ /dev/null @@ -1,76 +0,0 @@ -'use client' -import Image from 'next/image' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import soon from '../_assets/coming-soon.svg' -import mask from '../_assets/Mask group.webp' - - - -export default function Pins() { - - - return <> - - {/* 短效 */} - - - {`Mask 短效动态套餐 - - -
-

包时

-

- 当日可提取数量 - todo -

-
-
-
-

包量

-

- 剩余可提取数量 - todo -

-
-
-
- - {/* 长效 */} - - - {`Mask 长效动态套餐 - - - {/* {`coming -

敬请期待

*/} -
-

包时

-

- 当日可提取数量 - todo -

-
-
-
-

包量

-

- 剩余可提取数量 - todo -

-
-
-
- - {/* 固定 */} - - - 固定IP套餐 - - - {`coming -

敬请期待

-
-
- -} - diff --git a/src/app/admin/(dashboard)/_client/userCenter.tsx b/src/app/admin/(dashboard)/_client/userCenter.tsx new file mode 100644 index 0000000..4d88187 --- /dev/null +++ b/src/app/admin/(dashboard)/_client/userCenter.tsx @@ -0,0 +1,91 @@ +import Image from 'next/image' +import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card' +import {getProfile} from '@/actions/auth' +import {format} from 'date-fns' +import {CheckCircleIcon, CircleAlertIcon} from 'lucide-react' +import {Button, buttonVariants} from '@/components/ui/button' +import RechargeModal from '@/components/composites/recharge' +import {merge} from '@/lib/utils' +import Link from 'next/link' +import actionBill from '../_assets/action-bill.webp' +import actionBuy from '../_assets/action-buy.webp' +import actionLogout from '../_assets/action-logout.webp' + +async function UserCenter() { + const resp = await getProfile() + if (!resp.success) { + return ( +
+ 获取用户数据失败 +
+ ) + } + + const profile = resp.data + return ( + + +
+

{profile.phone}

+

{`最后登录:${format(profile.last_login, 'yyyy-MM-dd HH:mm')}`}

+
+
+ {profile.id_token + ? ( + <> +
+ + 已实名 +
+
+ {profile.name} + {profile.id_no} +
+ + ) + : ( + <> + + + 未实名 + + + + )} +
+
+

账户余额

+
+

+ ¥ + {profile.balance} +

+ +
+
+
+

快捷入口

+
+ + bill icon + 我的帐单 + + + buy icon + 购买产品 + + + logout icon + 个人中心 + +
+
+
+
+ ) +} +export default UserCenter diff --git a/src/app/admin/(dashboard)/layout.tsx b/src/app/admin/(dashboard)/layout.tsx index 77b4d0d..96969a1 100644 --- a/src/app/admin/(dashboard)/layout.tsx +++ b/src/app/admin/(dashboard)/layout.tsx @@ -1,6 +1,5 @@ - -import { ReactNode } from 'react' -import { Metadata } from 'next' +import {ReactNode} from 'react' +import {Metadata} from 'next' export async function generateMetadata(): Promise { return { @@ -14,4 +13,4 @@ export type BillsLayoutProps = { export default async function BillsLayout(props: BillsLayoutProps) { return props.children -} \ No newline at end of file +} diff --git a/src/app/admin/(dashboard)/page.tsx b/src/app/admin/(dashboard)/page.tsx index 418e951..b7961be 100644 --- a/src/app/admin/(dashboard)/page.tsx +++ b/src/app/admin/(dashboard)/page.tsx @@ -1,170 +1,191 @@ import Page from '@/components/page' import Image from 'next/image' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { getProfile } from '@/actions/auth' -import { format } from 'date-fns' -import { CheckCircleIcon, CircleAlertIcon } from 'lucide-react' -import { Button, buttonVariants } from '@/components/ui/button' -import RechargeModal from '@/components/composites/recharge' -import { merge } from '@/lib/utils' +import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card' +import {format} from 'date-fns' +import {merge} from '@/lib/utils' import banner from './_assets/banner.webp' -import actionBill from './_assets/action-bill.webp' -import actionBuy from './_assets/action-buy.webp' -import actionLogout from './_assets/action-logout.webp' -import Link from 'next/link' -import { listAnnouncements } from '@/actions/announcement' +import {listInitialization} from '@/actions/dashboard' import Charts from './_client/charts' -import Pins from './_client/pins' +import UserCenter from './_client/userCenter' +import soon from './_assets/coming-soon.svg' +import mask from './_assets/Mask group.webp' export type DashboardPageProps = {} -export default function DashboardPage(props: DashboardPageProps) { - return ( - - {/* banner */} -
- {`banner -
-

代理IP资源,先测后买

-

短效/长效/固定IP代理,一站式服务

-
-
- - {/* 磁贴集 */} - - - {/* 图表 */} - - - {/* 信息 */} - - - {/* 通知 */} - - -
- ) -} - -async function UserCenter() { - - const resp = await getProfile() - if (!resp.success) { - return ( -
- 获取用户数据失败 -
- ) - } - - const profile = resp.data - return ( - - -
-

{profile.phone}

-

{`最后登录:${format(profile.last_login, 'yyyy-MM-dd HH:mm')}`}

-
-
- {profile.id_token - ? <> -
- - 已实名 -
-
- {profile.name} - {profile.id_no} -
- - : <> - - - 未实名 - - - - } -
-
-

账户余额

-
-

¥{profile.balance}

- -
-
-
-

快捷入口

-
- - {`bill - 我的帐单 - - - {`buy - 购买产品 - - - {`logout - 个人中心 - -
-
-
-
- ) -} - -async function Announcements() { - - const resp = await listAnnouncements({ +export default async function DashboardPage(props: DashboardPageProps) { + const resp = await listInitialization({ page: 1, size: 5, }) if (!resp.success) { return ( -
+
获取公告数据失败
) } - const announcements = resp.data.list + const initData = resp.data return ( - + + {/* banner */} +
+ banner image +
+

代理IP资源,先测后买

+

短效/长效/固定IP代理,一站式服务

+
+
+ + {/* 磁贴集 */} + {initData && ( + + )} + + {/* 图表 */} + + + {/* 信息 */} + + + {/* 通知 */} + {initData && ( + + )} + +
+ ) +} +type DashboardChartProps = { + short_term: string + short_term_monthly: string + long_term: string + long_term_monthly: string +} +function Pins(props: DashboardChartProps) { + return ( + <> + + {/* 短效 */} + + + + Mask group + {' '} + 短效动态套餐 + + + +
+

包时

+

+ 当日可提取数量 + {props.short_term ? props.short_term : '1'} +

+
+
+
+

包量

+

+ 剩余可提取数量 + {props.short_term_monthly} +

+
+
+
+ + {/* 长效 */} + + + + Mask group + {' '} + 长效动态套餐 + + + + {/* {`coming +

敬请期待

*/} +
+

包时

+

+ 当日可提取数量 + {props.long_term} +

+
+
+
+

包量

+

+ 剩余可提取数量 + {props.long_term_monthly} +

+
+
+
+ + {/* 固定 */} + + + 固定IP套餐 + + + coming soon +

敬请期待

+
+
+ + ) +} + +type Props = { + list: { + id: number + title: string + created_at: Date + } [] +} +function Announcements(props: Props) { + return ( + -
+
公告 - 查看更多 + 查看更多
- - {announcements.length === 0 + + {!props.list.length ? ( -
- {/* {`coming -

暂无公告

*/} -
- ) - : announcements.map(item => ( -
-

{item.title}

-

{format(item.created_at, 'yyyy-MM-dd HH:mm')}

-
- )) - } +
+ coming soon +

暂无公告

+
+ ) + : props.list.map(item => ( +
+

{item.title}

+

{format(item.created_at, 'yyyy-MM-dd HH:mm')}

+
+ ))}
)