From a27e856f078fda747177b055162656660259be7b Mon Sep 17 00:00:00 2001 From: luorijun Date: Tue, 6 Jan 2026 14:57:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=94=A8=E6=88=B7=E8=AE=A4?= =?UTF-8?q?=E9=A2=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: luorijun --- src/actions/user.ts | 6 + src/app/(root)/(dashboard)/page.tsx | 271 ++++++++++++++++++---------- src/app/(root)/navigation.tsx | 14 +- src/app/(root)/user/page.tsx | 20 +- src/hooks/data.ts | 39 +++- 5 files changed, 242 insertions(+), 108 deletions(-) diff --git a/src/actions/user.ts b/src/actions/user.ts index fe30813..3c71c69 100644 --- a/src/actions/user.ts +++ b/src/actions/user.ts @@ -5,3 +5,9 @@ import { callByUser } from "./base" export async function getPageUsers(params: { page: number; size: number }) { return callByUser>("/api/admin/user/page", params) } + +export async function bindAdmin(params: { id: number }) { + return callByUser("/api/admin/user/bind", { + user_id: params.id, + }) +} diff --git a/src/app/(root)/(dashboard)/page.tsx b/src/app/(root)/(dashboard)/page.tsx index d4c0b1c..f3714fd 100644 --- a/src/app/(root)/(dashboard)/page.tsx +++ b/src/app/(root)/(dashboard)/page.tsx @@ -1,4 +1,4 @@ -import Link from 'next/link' +import Link from "next/link" export type DashboardPageProps = {} @@ -9,16 +9,18 @@ export default function DashboardPage(props: DashboardPageProps) {
-

IP代理管理控制台

-

上次更新: {new Date().toLocaleString('zh-CN')}

+

+ IP代理管理控制台 +

+

+ 上次更新: {new Date().toLocaleString("zh-CN")} +

- -
@@ -37,8 +39,18 @@ export default function DashboardPage(props: DashboardPageProps) { change="+12.5%" isIncrease={true} icon={ - - + + } /> @@ -48,9 +60,18 @@ export default function DashboardPage(props: DashboardPageProps) { change="+8.2%" isIncrease={true} icon={ - + + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" + /> } /> @@ -60,10 +81,18 @@ export default function DashboardPage(props: DashboardPageProps) { change="+2.4%" isIncrease={true} icon={ - + + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" + /> } /> @@ -73,10 +102,18 @@ export default function DashboardPage(props: DashboardPageProps) { change="-12.3%" isIncrease={true} icon={ - + + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" + /> } /> @@ -119,14 +156,18 @@ export default function DashboardPage(props: DashboardPageProps) {

活跃代理IP

- 查看全部 + + 查看全部 +
- + @@ -136,12 +177,18 @@ export default function DashboardPage(props: DashboardPageProps) { - {[1, 2, 3, 4, 5].map((item) => ( + {[1, 2, 3, 4, 5].map(item => ( @@ -150,8 +197,12 @@ export default function DashboardPage(props: DashboardPageProps) {
IP地址 位置 状态
- - + +
@@ -191,7 +242,9 @@ export default function DashboardPage(props: DashboardPageProps) {

告警通知

- 3 个新告警 + + 3 个新告警 +
@@ -215,8 +268,7 @@ export default function DashboardPage(props: DashboardPageProps) { />
-
@@ -227,35 +279,22 @@ export default function DashboardPage(props: DashboardPageProps) {

系统状态

- 运行正常 + + 运行正常 +
- - - - + + + +
+ className="block w-full py-2 text-center bg-gray-100 text-gray-600 border border-gray-200 rounded-md text-sm hover:bg-gray-200 transition-colors" + > 查看详细状态
@@ -274,11 +313,11 @@ function DataCard({ isIncrease, icon, }: { - title: string; - value: string; - change: string; - isIncrease: boolean; - icon: React.ReactNode; + title: string + value: string + change: string + isIncrease: boolean + icon: React.ReactNode }) { return (
@@ -287,14 +326,17 @@ function DataCard({

{title}

{value}

- + {change} 相比上周期
+ className={`p-3 rounded-md ${isIncrease ? "bg-blue-50 text-blue-600" : "bg-orange-50 text-orange-500"} border ${isIncrease ? "border-blue-100" : "border-orange-100"}`} + > {icon}
@@ -310,16 +352,22 @@ function ContentRow({ requests, successRate, }: { - ip: string; - location: string; - status: 'active' | 'warning' | 'error'; - requests: number; - successRate: string; + ip: string + location: string + status: "active" | "warning" | "error" + requests: number + successRate: string }) { const statusConfig = { - active: {color: 'bg-green-100 text-green-800 border-green-200', label: '在线'}, - warning: {color: 'bg-yellow-100 text-yellow-800 border-yellow-200', label: '不稳定'}, - error: {color: 'bg-red-100 text-red-800 border-red-200', label: '离线'}, + active: { + color: "bg-green-100 text-green-800 border-green-200", + label: "在线", + }, + warning: { + color: "bg-yellow-100 text-yellow-800 border-yellow-200", + label: "不稳定", + }, + error: { color: "bg-red-100 text-red-800 border-red-200", label: "离线" }, } return ( @@ -331,36 +379,67 @@ function ContentRow({
{location}
- + {statusConfig[status].label} {requests.toLocaleString()} - - {successRate} - + {successRate}
@@ -376,22 +455,24 @@ function AlertItem({ time, message, }: { - title: string; - severity: 'high' | 'medium' | 'low'; - time: string; - message: string; + title: string + severity: "high" | "medium" | "low" + time: string + message: string }) { const severityConfig = { - high: {color: 'bg-red-50 border-red-200', dot: 'bg-red-500'}, - medium: {color: 'bg-yellow-50 border-yellow-200', dot: 'bg-yellow-500'}, - low: {color: 'bg-blue-50 border-blue-200', dot: 'bg-blue-500'}, + high: { color: "bg-red-50 border-red-200", dot: "bg-red-500" }, + medium: { color: "bg-yellow-50 border-yellow-200", dot: "bg-yellow-500" }, + low: { color: "bg-blue-50 border-blue-200", dot: "bg-blue-500" }, } return (
- + {title}
{time} @@ -407,14 +488,14 @@ function StatusBar({ value, status, }: { - title: string; - value: number; - status: 'normal' | 'warning' | 'error'; + title: string + value: number + status: "normal" | "warning" | "error" }) { const statusConfig = { - normal: {color: 'bg-green-500', bgColor: 'bg-green-100'}, - warning: {color: 'bg-yellow-500', bgColor: 'bg-yellow-100'}, - error: {color: 'bg-red-500', bgColor: 'bg-red-100'}, + normal: { color: "bg-green-500", bgColor: "bg-green-100" }, + warning: { color: "bg-yellow-500", bgColor: "bg-yellow-100" }, + error: { color: "bg-red-500", bgColor: "bg-red-100" }, } return ( @@ -423,10 +504,12 @@ function StatusBar({ {title} {value}%
-
+
diff --git a/src/app/(root)/navigation.tsx b/src/app/(root)/navigation.tsx index 3e21735..c7ad877 100644 --- a/src/app/(root)/navigation.tsx +++ b/src/app/(root)/navigation.tsx @@ -95,7 +95,7 @@ function NavItem({ href, icon: Icon, label }: NavItemProps) { : "text-muted-foreground hover:bg-accent hover:text-accent-foreground" }`} > - + {!collapsed && {label}} ) @@ -175,13 +175,13 @@ export default function Navigation() { - + {/**/} {/* IP 资源 */} - + {/* - + */} @@ -205,9 +205,9 @@ export default function Navigation() { {/* 系统 */} - - - + {/**/} + + {/**/} diff --git a/src/app/(root)/user/page.tsx b/src/app/(root)/user/page.tsx index f6db39a..a6cf0f2 100644 --- a/src/app/(root)/user/page.tsx +++ b/src/app/(root)/user/page.tsx @@ -1,13 +1,17 @@ "use client" import { Suspense } from "react" -import { getPageUsers } from "@/actions/user" +import { bindAdmin, getPageUsers } from "@/actions/user" import { DataTable, useDataTable } from "@/components/data-table" import { Button } from "@/components/ui/button" +import { useFetch } from "@/hooks/data" import type { User } from "@/models/user" export default function UserPage() { const table = useDataTable((page, size) => getPageUsers({ page, size })) - + const bind = useFetch((id: number) => bindAdmin({ id }), { + done: "用户已认领", + fail: "用户认领失败", + }) return (
@@ -27,10 +31,14 @@ export default function UserPage() { { header: "创建时间", accessorKey: "created_at" }, { header: "操作", - cell: () => ( -
- -
+ cell: ctx => ( + ), }, ]} diff --git a/src/hooks/data.ts b/src/hooks/data.ts index 33b4f39..eac5747 100644 --- a/src/hooks/data.ts +++ b/src/hooks/data.ts @@ -1,5 +1,42 @@ -import { useState } from "react" +import { + type Dispatch, + type SetStateAction, + useCallback, + useState, +} from "react" +import { toast } from "sonner" +import type { ApiResponse } from "@/lib/api" export function useStatus() { return useState<"load" | "fail" | "done">("load") } + +export function useFetch( + fetchData: (...args: TArgs) => Promise>, + messages: { + done?: string + fail?: string + }, + setStatus?: Dispatch>, +) { + return useCallback( + async (...args: TArgs) => { + try { + setStatus?.("load") + const resp = await fetchData(...args) + if (!resp.success) { + throw new Error(resp.message) + } + + setStatus?.("done") + toast.success(messages.done || "获取数据成功") + } catch (e) { + setStatus?.("fail") + toast.error(messages.fail || "获取数据失败", { + description: (e as Error).message || "未知错误", + }) + } + }, + [fetchData, setStatus, messages], + ) +}