首页添加二级路由

This commit is contained in:
Eamon
2026-08-03 10:53:12 +08:00
parent 0d5e28c785
commit 038e66d0cc
18 changed files with 1240 additions and 59 deletions

View File

@@ -1,5 +1,305 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { useMemo, useState } from "react"
import { Controller, useForm } from "react-hook-form"
import { Link } from "react-router-dom"
import { toast } from "sonner"
import { z } from "zod"
import DataTable from "@/components/data-table"
import Page from "@/components/page"
import { Button } from "@/components/ui/button"
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import type { ChannelRecord } from "@/lib/models/channel"
const filterSchema = z.object({
expired_at_end: z.string().optional(),
product: z.string().optional(),
plan: z.string().optional(),
account: z.string().optional(),
status: z.string().optional(),
})
type FormValues = z.infer<typeof filterSchema>
const initialData: ChannelRecord[] = [
{
id: "1",
startAt: "2025.04.09",
product: "极光IP",
plan: "测试卡(带宽)",
account: "ft8765",
password: "765修改",
ip: "甘肃-兰州125.75.111.127",
server: "p3.125.74.129.38.sdip.cc",
connections: 1,
expiredAt: "2025.04.09",
remaining: "已过期",
online: "查看",
download: "下载",
status: "",
},
{
id: "2",
startAt: "2025.04.09",
product: "极光IP",
plan: "测试卡(带宽)",
account: "ft8765",
password: "765修改",
ip: "辽宁-辽阳123.190.161.192",
server: "p16.cqdwx4.sdip.cc",
connections: 1,
expiredAt: "2025.04.09",
remaining: "0天00时37分",
online: "查看",
download: "下载",
status: "",
},
{
id: "3",
startAt: "2025.04.09",
product: "极光IP",
plan: "静态-项目-20M-月卡",
account: "ft8765",
password: "765修改",
ip: "黑龙江-鸡西100.100.4.249",
server: "p37.sdlw2.sdip.cc",
connections: 1,
expiredAt: "2025.04.09",
remaining: "0天00时37分",
online: "查看",
download: "下载",
status: "",
},
]
export default function ChannelsPage() {
return <Page>IP管理</Page>
const [filterValues, setFilterValues] = useState<FormValues>({})
const [status, setStatus] = useState<"load" | "done">("done")
// 行选择状态
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({})
const { control, handleSubmit, reset } = useForm<FormValues>({
resolver: zodResolver(filterSchema),
defaultValues: {
expired_at_end: "",
product: "",
plan: "",
account: "",
status: "",
},
})
const filteredData = useMemo(() => {
if (!filterValues || Object.values(filterValues).every(value => !value)) {
return initialData
}
return initialData.filter(record => {
if (filterValues.expired_at_end) {
return record.expiredAt <= filterValues.expired_at_end
}
if (
filterValues.product &&
!record.product.includes(filterValues.product)
) {
return false
}
if (filterValues.plan && !record.plan.includes(filterValues.plan)) {
return false
}
if (
filterValues.account &&
!record.account.includes(filterValues.account)
) {
return false
}
if (filterValues.status && record.status !== filterValues.status) {
return false
}
return true
})
}, [filterValues])
const onSubmit = (values: FormValues) => {
setStatus("load")
setTimeout(() => {
setFilterValues(values)
setRowSelection({})
setStatus("done")
}, 200)
}
const onReset = () => {
reset()
setFilterValues({})
setRowSelection({})
}
// 根据选中ID获取对应行数据
const getSelectedRows = () => {
const ids = Object.keys(rowSelection)
return filteredData.filter(item => ids.includes(item.id))
}
// 批量续费
const handleRenew = () => {
const rows = getSelectedRows()
console.log("续费选中数据:", rows)
}
// 单个退货
const handleReturn = () => {
const rows = getSelectedRows()
if (rows.length !== 1) {
toast.error("请选择一条数据进行退货")
return
}
console.log("退货数据:", rows[0])
}
return (
<Page>
<div>
<form className="bg-card rounded-lg" onSubmit={handleSubmit(onSubmit)}>
<div className="flex items-center justify-center gap-4 bg-amber-200 px-3 py-3">
<span className="text-orange-600 text-sm">
为了给您带来更好的服务体验,请完善QQ号和微信号
</span>
<Link to="/">立即完善</Link>
</div>
<div className="flex flex-wrap p-4 items-end gap-4">
<Controller
name="expired_at_end"
control={control}
render={({ field, fieldState }) => (
<Field
data-invalid={fieldState.invalid}
className="w-48 flex-none"
>
<FieldLabel>到期时间</FieldLabel>
<Input type="datetime-local" step="1" {...field} />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="product"
control={control}
render={({ field, fieldState }) => (
<Field
data-invalid={fieldState.invalid}
className="w-48 flex-none"
>
<FieldLabel>全部产品</FieldLabel>
<Input {...field} placeholder="请输入产品名称" />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="plan"
control={control}
render={({ field, fieldState }) => (
<Field
data-invalid={fieldState.invalid}
className="w-48 flex-none"
>
<FieldLabel>全部套餐</FieldLabel>
<Input {...field} placeholder="请输入套餐名称" />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="account"
control={control}
render={({ field, fieldState }) => (
<Field
data-invalid={fieldState.invalid}
className="w-40 flex-none"
>
<FieldLabel>账号</FieldLabel>
<Input {...field} placeholder="请输入账号" />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<div className="flex gap-2">
<Button type="submit">查询</Button>
<Button type="button" variant="outline" onClick={onReset}>
重置
</Button>
</div>
</div>
</form>
</div>
<div className="bg-card rounded-lg p-4">
<div className="flex gap-4 items-center">
<Button type="button" variant="outline" onClick={handleRenew}>
续费
</Button>
<Button type="button" variant="outline" onClick={handleReturn}>
退货
</Button>
<p className="text-orange-600 text-sm">
温馨提示:批量续费可通过筛选和搜索功能精准定位账号
</p>
</div>
<DataTable<ChannelRecord>
data={filteredData}
status={status}
columns={[
{ accessorKey: "startAt", header: "开通时间" },
{ accessorKey: "product", header: "产品" },
{ accessorKey: "plan", header: "套餐" },
{ accessorKey: "account", header: "账号" },
{ accessorKey: "password", header: "密码" },
{ accessorKey: "ip", header: "IP地址" },
{ accessorKey: "server", header: "服务器" },
{ accessorKey: "connections", header: "连接数" },
{ accessorKey: "expiredAt", header: "到期时间" },
{ accessorKey: "remaining", header: "剩余时间" },
{
accessorKey: "online",
header: "在线及拨线",
cell: ({ row }) => (
<Button size="sm" variant="outline">
{row.original.online}
</Button>
),
},
{ accessorKey: "status", header: "选择地区" },
{
accessorKey: "download",
header: "软件下载",
cell: ({ row }) => (
<Button size="sm" variant="ghost">
{row.original.download}
</Button>
),
},
]}
pagination={{
page: 0,
size: 10,
total: filteredData.length,
}}
enableSelection
getRowId={row => row.id}
rowSelection={rowSelection}
onSelectionChange={setRowSelection}
/>
</div>
</Page>
)
}