diff --git a/src/actions/dashboard.ts b/src/actions/dashboard.ts new file mode 100644 index 0000000..429d020 --- /dev/null +++ b/src/actions/dashboard.ts @@ -0,0 +1,23 @@ +"use server" + +import type { + DashboardExtractable, + DashboardOverview, + DashboardTrendItem, +} from "@/models/dashboard" +import { callByUser } from "./base" + +export async function getOverview() { + return callByUser("/api/admin/home/overview", {}) +} + +export async function getTrend(params: { + date_start: string + date_end: string +}) { + return callByUser("/api/admin/home/trend", params) +} + +export async function getExtractable() { + return callByUser("/api/admin/home/extractable", {}) +} diff --git a/src/app/(root)/(dashboard)/page.tsx b/src/app/(root)/(dashboard)/page.tsx index 66f8ff2..89827bf 100644 --- a/src/app/(root)/(dashboard)/page.tsx +++ b/src/app/(root)/(dashboard)/page.tsx @@ -6,7 +6,6 @@ import { ArrowDownRight, ArrowUpRight, BarChart3, - Calendar, DoorOpenIcon, Server, TrendingUp, @@ -14,13 +13,19 @@ import { Users, } from "lucide-react" import Link from "next/link" -import { useMemo, useState } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" +import { getExtractable, getOverview, getTrend } from "@/actions/dashboard" import { SimpleBarChart, SimpleHorizontalBarChart, SimpleLineChart, } from "@/components/charts" import { Page } from "@/components/page" +import type { + DashboardExtractable, + DashboardOverview, + DashboardTrendItem, +} from "@/models/dashboard" type TimeRange = "today" | "7d" | "30d" | "90d" | "custom" @@ -32,6 +37,35 @@ const timeRangeLabels: Record = { custom: "自定义", } +function getDateRange( + range: TimeRange, + customStart: string, + customEnd: string, +) { + const today = format(new Date(), "yyyy-MM-dd") + switch (range) { + case "today": + return { date_start: today, date_end: today } + case "7d": + return { + date_start: format(subDays(new Date(), 6), "yyyy-MM-dd"), + date_end: today, + } + case "30d": + return { + date_start: format(subDays(new Date(), 29), "yyyy-MM-dd"), + date_end: today, + } + case "90d": + return { + date_start: format(subDays(new Date(), 89), "yyyy-MM-dd"), + date_end: today, + } + case "custom": + return { date_start: customStart, date_end: customEnd } + } +} + function mulberry32(seed: number) { let t = seed return () => { @@ -43,85 +77,113 @@ function mulberry32(seed: number) { } } +const mockUserData = (() => { + const rng = mulberry32(42) + return [ + { name: "张三", activity: Math.floor(rng() * 100) + 200 }, + { name: "李四", activity: Math.floor(rng() * 100) + 180 }, + { name: "王五", activity: Math.floor(rng() * 100) + 160 }, + { name: "赵六", activity: Math.floor(rng() * 100) + 140 }, + { name: "孙七", activity: Math.floor(rng() * 100) + 120 }, + { name: "周八", activity: Math.floor(rng() * 100) + 90 }, + { name: "吴九", activity: Math.floor(rng() * 100) + 60 }, + ] +})() + export default function DashboardPage() { const [timeRange, setTimeRange] = useState("7d") const [showCustomPicker, setShowCustomPicker] = useState(false) const [startDate, setStartDate] = useState("") const [endDate, setEndDate] = useState("") - const chartData = useMemo(() => { - const days = 14 - const seed = 42 - const rng = mulberry32(seed) - const proxyData = [] - const tradeData = [] - const customerData = [] - const extractData = [] + const [overview, setOverview] = useState(null) + const [trendData, setTrendData] = useState([]) + const [extractable, setExtractable] = useState( + null, + ) + const [lastUpdated, setLastUpdated] = useState("--") - for (let i = days - 1; i >= 0; i--) { - const date = subDays(new Date(), i) - const dateStr = format(date, "MM-dd") - proxyData.push({ - date: dateStr, - value: Math.floor(rng() * 50) + 80, - }) - tradeData.push({ - date: dateStr, - value: Math.floor(rng() * 3000) + 5000, - }) - customerData.push({ - date: dateStr, - value: Math.floor(rng() * 20) + 5, - }) - extractData.push({ - date: dateStr, - value: Math.floor(rng() * 800) + 1200, - }) - } + const fetchData = useCallback( + async (range: TimeRange, customStart: string, customEnd: string) => { + const [overviewResp, trendResp, extractableResp] = await Promise.all([ + getOverview(), + getTrend(getDateRange(range, customStart, customEnd)), + getExtractable(), + ]) + console.log(overviewResp, "overviewResp") + console.log(trendResp, "trendResp") + console.log(extractableResp, "extractableResp") - const userData = [ - { name: "张三", activity: 287 }, - { name: "李四", activity: 245 }, - { name: "王五", activity: 198 }, - { name: "赵六", activity: 156 }, - { name: "孙七", activity: 132 }, - { name: "周八", activity: 98 }, - { name: "吴九", activity: 76 }, - ] + if (overviewResp.success) setOverview(overviewResp.data) + if (trendResp.success) setTrendData(trendResp.data) + if (extractableResp.success) setExtractable(extractableResp.data) + setLastUpdated(format(new Date(), "yyyy-MM-dd HH:mm:ss")) + }, + [], + ) - return { proxyData, tradeData, customerData, extractData, userData } - }, []) + useEffect(() => { + fetchData(timeRange, startDate, endDate) + }, [fetchData, timeRange, startDate, endDate]) - // 处理自定义日期按钮点击 - const handleCustomClick = () => { - setShowCustomPicker(true) - setTimeRange("custom") - // 清空之前的日期 - setStartDate("") - setEndDate("") - } + const handleTimeRangeChange = useCallback( + (key: TimeRange) => { + if (key === "custom") { + setShowCustomPicker(true) + setTimeRange("custom") + setStartDate("") + setEndDate("") + } else { + setTimeRange(key) + setShowCustomPicker(false) + fetchData(key, "", "") + } + }, + [fetchData], + ) - // 处理日期确认 const handleDateConfirm = () => { if (startDate && endDate) { - console.log("自定义日期范围:", startDate, "~", endDate) setShowCustomPicker(false) + fetchData("custom", startDate, endDate) } } - // 处理取消 const handleCancel = () => { setShowCustomPicker(false) setTimeRange("7d") + fetchData("7d", "", "") } + const chartData = useMemo(() => { + const proxyData = trendData.map(item => ({ + date: item.date.slice(5), + value: item.active_channel_count, + })) + const tradeData = trendData.map(item => ({ + date: item.date.slice(5), + value: Number.parseFloat(item.trade_amount) || 0, + })) + const customerData = trendData.map(item => ({ + date: item.date.slice(5), + value: item.new_customer_count, + })) + const extractData = trendData.map(item => ({ + date: item.date.slice(5), + value: item.extract_count, + })) + + return { proxyData, tradeData, customerData, extractData } + }, [trendData]) + return ( - {/* 欢迎栏 */}

IP代理管理控制台

-

上次更新: --

+

+ 上次更新: {lastUpdated} +

- {/* 指标卡片 */}
- {/* 时间筛选栏 */}
数据周期 @@ -209,14 +273,7 @@ export default function DashboardPage() { {(Object.keys(timeRangeLabels) as TimeRange[]).map(key => (
- {/* 点击自定义时显示 */} {showCustomPicker && (
- {/* 图表区域 */}
- {/* 每日活跃代理 */}

每日活跃代理

- {/* 每日交易额 */}

每日交易额

- {/* 每日新增客户 */}

每日新增客户

- {/* 每日提取量 */}

每日提取量

- {/*套餐统计 */}
@@ -326,31 +376,32 @@ export default function DashboardPage() {
- 总可用IP - -- + 总可提取量 + + {extractable ? extractable.long + extractable.short : "--"} +
- {/* 最近活跃用户 */}

最近活跃用户

+ ( + + 过期时间 + + {fieldState.error?.message} + + )} + /> )} /> - ( - - 开始时间 - - {fieldState.error?.message} - - )} - /> - ( @@ -142,7 +143,7 @@ export default function BalancePage() { {fieldState.error?.message} )} - /> + /> */}