diff --git a/README.md b/README.md index ffc633a..76c88a2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 提取后刷新提取页套餐可用余量 -提取 ip 认证 +提取时检查 IP 和实名状态 保存客户端信息时用 jwt 序列化 diff --git a/package.json b/package.json index 45416c7..055d47b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@radix-ui/react-radio-group": "^1.2.3", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.4", "@tanstack/react-table": "^8.21.2", "canvas": "^3.1.0", "class-variance-authority": "^0.7.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b3c8a1..80277a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@radix-ui/react-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-tabs': + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/react-table': specifier: ^8.21.2 version: 8.21.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -484,6 +487,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.3': + resolution: {integrity: sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.1.1': resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} peerDependencies: @@ -542,6 +558,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.1.5': resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} peerDependencies: @@ -799,6 +824,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-roving-focus@1.1.3': + resolution: {integrity: sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-select@2.1.6': resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==} peerDependencies: @@ -830,6 +868,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-tabs@1.1.4': + resolution: {integrity: sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.0': resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: @@ -2935,6 +2986,18 @@ snapshots: '@types/react': 19.0.10 '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-collection@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)': dependencies: react: 19.0.0 @@ -2987,6 +3050,12 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + '@radix-ui/react-direction@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -3232,6 +3301,23 @@ snapshots: '@types/react': 19.0.10 '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-roving-focus@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-select@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/number': 1.1.0 @@ -3275,6 +3361,22 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + '@radix-ui/react-tabs@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)': dependencies: react: 19.0.0 diff --git a/src/app/admin/(dashboard)/_assets/banner.webp b/src/app/admin/(dashboard)/_assets/banner.webp new file mode 100644 index 0000000..6cf0c70 Binary files /dev/null and b/src/app/admin/(dashboard)/_assets/banner.webp differ diff --git a/src/app/admin/(dashboard)/page.tsx b/src/app/admin/(dashboard)/page.tsx index 9ba6a9d..b44f8e9 100644 --- a/src/app/admin/(dashboard)/page.tsx +++ b/src/app/admin/(dashboard)/page.tsx @@ -1,43 +1,104 @@ import Page from '@/components/page' +import Image from 'next/image' +import banner from './_assets/banner.webp' +import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card' +import {Button} from '@/components/ui/button' +import {getProfile} from '@/actions/auth/auth' +import {redirect} from 'next/navigation' +import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs' export type DashboardPageProps = {} export default async function DashboardPage(props: DashboardPageProps) { - return ( - - {/* banner */} -
+ const profile = await getProfile() + if (!profile) { + return redirect('/login') + } + return ( + + {/* banner */} +
+ {`banner +
+

代理IP资源,先测后买

+

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

+
{/* 短效 */} -
- -
+ + + 短效动态套餐 + + +
+

包时

+

+ 套餐数量 + todo +

+ +
+
+

包量

+

+ 套餐数量 + todo +

+ +
+
+
{/* 长效 */} -
- -
+ + + 长效动态套餐 + + + todo + + {/* 固定 */} -
- -
+ + + 固定IP套餐 + + + todo + + {/* 图表 */} -
- +
+ + + 动态 IP 套餐 + 静态 IP 套餐 + + + dynamic + + + static + +
{/* 信息 */} -
- -
+ + + 个人中心 + + {/* 通知 */} -
- -
+ + + 待办事项 + + ) } diff --git a/src/app/globals.css b/src/app/globals.css index 78aac68..082e65e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3,13 +3,15 @@ @plugin "tailwindcss-animate"; :root { - --background: oklch(1 0 0); - --foreground: oklch(0.25 0 0); - --weak: oklch(0.5 0 0); + --idle: oklch(1 0 0); + --idle-muted: oklch(0.965 0 0); + --idle-text: oklch(0.25 0 0); + --idle-desc: oklch(0.5 0 0); --primary: oklch(0.65 0.16 265); + --primary-muted: oklch(0.965 0.024 265); --primary-text: oklch(1 0 0); - --primary-weak: oklch(0.5 0 0); + --primary-desc: oklch(0.5 0 0); --secondary: oklch(0.965 0 0); --secondary-text: oklch(0.25 0 0); @@ -29,9 +31,6 @@ --card: oklch(0.985 0 0); --card-text: oklch(0.25 0 0); - --muted: oklch(0.965 0 0); - --muted-text: oklch(0.25 0 0); - --popover: oklch(1 0 0); --popover-foreground: oklch(0.25 0 0); --border: oklch(0.928 0.006 264.531); @@ -53,13 +52,16 @@ } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-weak: var(--weak); + --color-background: var(--idle); + --color-foreground: var(--idle-text); + --color-weak: var(--idle-desc); + --color-muted: var(--idle-muted); + --color-muted-foreground: var(--idle-text); --color-primary: var(--primary); + --color-primary-muted: var(--primary-muted); --color-primary-foreground: var(--primary-text); - --color-primary-weak: var(--primary-weak); + --color-primary-weak: var(--primary-desc); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-text); @@ -80,8 +82,6 @@ --color-card-foreground: var(--card-text); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-text); --color-destructive: var(--fail); --color-border: var(--border); --color-input: var(--input); diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index c3cc7b4..1db263a 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
) {
) { return (
) diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..ee2aa2b --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { merge } from "@/lib/utils" + +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent }