修复购买到支付流程的接口和页面&数据展示
This commit is contained in:
7
src/actions/batch.ts
Normal file
7
src/actions/batch.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { PageRecord } from "@/lib/api"
|
||||
import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function getPageBatch(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/batch/page", params)
|
||||
}
|
||||
7
src/actions/bill.ts
Normal file
7
src/actions/bill.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { PageRecord } from "@/lib/api"
|
||||
import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function getPageBill(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/bill/page", params)
|
||||
}
|
||||
7
src/actions/channel.ts
Normal file
7
src/actions/channel.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { PageRecord } from "@/lib/api"
|
||||
import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function getPageChannel(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/channel/page", params)
|
||||
}
|
||||
14
src/actions/resources.ts
Normal file
14
src/actions/resources.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { PageRecord } from "@/lib/api"
|
||||
import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function listResourceLong(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/resource/long/page", params)
|
||||
}
|
||||
|
||||
export async function listResourceShort(params: {
|
||||
page: number
|
||||
size: number
|
||||
}) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/resource/short/page", params)
|
||||
}
|
||||
7
src/actions/trade.ts
Normal file
7
src/actions/trade.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { PageRecord } from "@/lib/api"
|
||||
import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function getPageTrade(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/admin/trade/page", params)
|
||||
}
|
||||
@@ -3,5 +3,5 @@ import type { User } from "@/models/user"
|
||||
import { callByUser } from "./base"
|
||||
|
||||
export async function getPageUsers(params: { page: number; size: number }) {
|
||||
return callByUser<PageRecord<User>>("/api/user/page", params)
|
||||
return callByUser<PageRecord<User>>("/api/admin/user/page", params)
|
||||
}
|
||||
|
||||
29
src/app/(root)/batch/page.tsx
Normal file
29
src/app/(root)/batch/page.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { getPageBatch } from "@/actions/batch"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
const table = useDataTable<User>((page, size) => getPageBatch({ page, size }))
|
||||
console.log(table, "table")
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "批次号", accessorKey: "batch_no" },
|
||||
{ header: "城市", accessorKey: "city" },
|
||||
{ header: "省份", accessorKey: "prov" },
|
||||
{ header: "数量", accessorKey: "count" },
|
||||
{ header: "提取IP", accessorKey: "ip" },
|
||||
{ header: "运营商", accessorKey: "isp" },
|
||||
{ header: "可用资源", accessorKey: "resource_id" },
|
||||
{ header: "时间", accessorKey: "time" },
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
28
src/app/(root)/billing/page.tsx
Normal file
28
src/app/(root)/billing/page.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { getPageBill } from "@/actions/bill"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
const table = useDataTable<User>((page, size) => getPageBill({ page, size }))
|
||||
console.log(table, "table")
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "账单号", accessorKey: "bill_no" },
|
||||
{ header: "信息", accessorKey: "info" },
|
||||
{ header: "金额", accessorKey: "amount" },
|
||||
{ header: "可用资源", accessorKey: "resource_id" },
|
||||
{ header: "类型", accessorKey: "type" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{ header: "更新时间", accessorKey: "updated_at" },
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
37
src/app/(root)/channel/page.tsx
Normal file
37
src/app/(root)/channel/page.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { getPageChannel } from "@/actions/channel"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
const table = useDataTable<User>((page, size) =>
|
||||
getPageChannel({ page, size }),
|
||||
)
|
||||
console.log(table, "table")
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "批次号", accessorKey: "batch_no" },
|
||||
{ header: "边缘节点", accessorKey: "edge_ref" },
|
||||
{ header: "省份", accessorKey: "filter_prov" },
|
||||
{ header: "城市", accessorKey: "filter_city" },
|
||||
{ header: "运营商", accessorKey: "filter_isp" },
|
||||
{ header: "主机", accessorKey: "host" },
|
||||
{ header: "端口", accessorKey: "port" },
|
||||
{ header: "密码", accessorKey: "password" },
|
||||
{ header: "代理号", accessorKey: "proxy_id" },
|
||||
{ header: "可用资源", accessorKey: "resource_id" },
|
||||
{ header: "用户名", accessorKey: "username" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{ header: "更新时间", accessorKey: "updated_at" },
|
||||
{ header: "过期时间", accessorKey: "expired_at" },
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
@@ -188,17 +188,17 @@ export default function Navigation() {
|
||||
{/* 客户 */}
|
||||
<NavGroup title="客户">
|
||||
<NavItem href="/user" icon={Users} label="客户管理" />
|
||||
<NavItem href="/resources" icon={Package} label="套餐管理" />
|
||||
<NavItem href="/orders" icon={ClipboardList} label="订单管理" />
|
||||
<NavItem href="/trade" icon={Activity} label="交易明细" />
|
||||
<NavItem href="/billing" icon={DollarSign} label="账单详情" />
|
||||
</NavGroup>
|
||||
|
||||
<NavSeparator />
|
||||
|
||||
{/* 运营 */}
|
||||
<NavGroup title="运营">
|
||||
<NavItem href="/api/management" icon={Code} label="API管理" />
|
||||
<NavItem href="/traffic" icon={Activity} label="流量监控" />
|
||||
<NavItem href="/billing" icon={DollarSign} label="计费系统" />
|
||||
<NavItem href="/resources" icon={Package} label="套餐管理" />
|
||||
<NavItem href="/batch" icon={ClipboardList} label="使用记录" />
|
||||
<NavItem href="/channel" icon={Code} label="IP管理" />
|
||||
</NavGroup>
|
||||
|
||||
<NavSeparator />
|
||||
|
||||
62
src/app/(root)/resources/page.tsx
Normal file
62
src/app/(root)/resources/page.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { listResourceLong, listResourceShort } from "@/actions/resources"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
return (
|
||||
<div>
|
||||
<Tabs defaultValue="short">
|
||||
<TabsList className="bg-card p-1.5 rounded-lg">
|
||||
<TabsTrigger
|
||||
value="short"
|
||||
className="w-30 h-9 data-[state=active]:bg-primary-muted text-base rounded-md"
|
||||
>
|
||||
短效套餐
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="long"
|
||||
className="w-30 h-9 data-[state=active]:bg-primary-muted text-base rounded-md"
|
||||
>
|
||||
长效套餐
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="short" className="flex flex-col gap-4">
|
||||
<ResourceList resourceType="short" />
|
||||
</TabsContent>
|
||||
<TabsContent value="long" className="flex flex-col gap-4">
|
||||
<ResourceList resourceType="long" />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface ResourceListProps {
|
||||
resourceType: "long" | "short"
|
||||
}
|
||||
|
||||
function ResourceList({ resourceType }: ResourceListProps) {
|
||||
const isLong = resourceType === "long"
|
||||
const listFn = isLong ? listResourceLong : listResourceShort
|
||||
const table = useDataTable<User>((page, size) => listFn({ page, size }))
|
||||
console.log(table, "table")
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "套餐编号", accessorKey: "resource_no" },
|
||||
{ header: "状态", accessorKey: "active" },
|
||||
{ header: "类型", accessorKey: "type" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{ header: "更新时间", accessorKey: "updated_at" },
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
32
src/app/(root)/trade/page.tsx
Normal file
32
src/app/(root)/trade/page.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { getPageTrade } from "@/actions/trade"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
const table = useDataTable<User>((page, size) => getPageTrade({ page, size }))
|
||||
console.log(table, "table")
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "套餐号", accessorKey: "inner_no" },
|
||||
{ header: "支付方式", accessorKey: "method" },
|
||||
{ header: "支付金额", accessorKey: "payment" },
|
||||
{ header: "支付平台", accessorKey: "platform" },
|
||||
{ header: "已退款", accessorKey: "refunded" },
|
||||
{ header: "支付状态", accessorKey: "status" },
|
||||
{ header: "购买套餐", accessorKey: "subject" },
|
||||
{ header: "类型", accessorKey: "type" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{ header: "更新时间", accessorKey: "updated_at" },
|
||||
{ header: "过期时间", accessorKey: "canceled_at" },
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
import { Suspense } from "react"
|
||||
import { getPageUsers } from "@/actions/user"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -6,32 +7,35 @@ import type { User } from "@/models/user"
|
||||
|
||||
export default function UserPage() {
|
||||
const table = useDataTable<User>((page, size) => getPageUsers({ page, size }))
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "账号", accessorKey: "username" },
|
||||
{ header: "手机", accessorKey: "phone" },
|
||||
{ header: "邮箱", accessorKey: "email" },
|
||||
{ header: "姓名", accessorKey: "name" },
|
||||
{ header: "余额", accessorKey: "balance" },
|
||||
{ header: "认证状态", accessorKey: "id_type" },
|
||||
{ header: "账号状态", accessorKey: "status" },
|
||||
{ header: "联系方式", accessorKey: "contact_wechat" },
|
||||
{ header: "管理员", accessorKey: "admin_id" },
|
||||
{ header: "最后登录时间", accessorKey: "last_login" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{
|
||||
header: "操作",
|
||||
cell: () => (
|
||||
<div>
|
||||
<Button>认领</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
columns={[
|
||||
{ header: "账号", accessorKey: "username" },
|
||||
{ header: "手机", accessorKey: "phone" },
|
||||
{ header: "邮箱", accessorKey: "email" },
|
||||
{ header: "姓名", accessorKey: "name" },
|
||||
{ header: "余额", accessorKey: "balance" },
|
||||
{ header: "认证状态", accessorKey: "id_type" },
|
||||
{ header: "账号状态", accessorKey: "status" },
|
||||
{ header: "联系方式", accessorKey: "contact_wechat" },
|
||||
{ header: "管理员", accessorKey: "admin_id" },
|
||||
{ header: "最后登录时间", accessorKey: "last_login" },
|
||||
{ header: "创建时间", accessorKey: "created_at" },
|
||||
{
|
||||
header: "操作",
|
||||
cell: () => (
|
||||
<div>
|
||||
<Button>认领</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
66
src/components/ui/tabs.tsx
Normal file
66
src/components/ui/tabs.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Tabs({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
return (
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
Reference in New Issue
Block a user