Files
juip-web/src/admin/whitelist/page.tsx
2026-09-01 10:22:56 +08:00

128 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Lock, Trash2, Unlock } from "lucide-react"
import { useState } from "react"
import DataTable from "@/components/data-table"
import Page from "@/components/page"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import type { WhiteIpItem } from "@/lib/models/whitelist"
// TODO: 后端白名单接口就绪后,将 saveWhiteIp/fetchWhiteList 接入(见 src/api/whitelist.ts)
export default function WhitelistPage() {
const [ip, setIp] = useState("")
const [list, setList] = useState<WhiteIpItem[]>([])
const handleSave = () => {
const value = ip.trim()
if (!value) return
// TODO: await saveWhiteIp(value)
setList(prev => [
{ id: Date.now(), ip: value, createTime: "", isLocked: "未锁定" },
...prev,
])
setIp("")
}
return (
<Page>
<div className="bg-white rounded-lg p-4">
<div className="space-y-4">
<div className="flex items-center gap-3">
<Input
value={ip}
onChange={e => setIp(e.target.value)}
onKeyDown={e => e.key === "Enter" && handleSave()}
placeholder="请输入IP地址并点击保存白名单"
className="max-w-sm"
/>
<Button onClick={handleSave}>保存</Button>
</div>
<div className="flex items-center gap-3">
<h3 className="font-semibold text-lg">当前白名单列表</h3>
<Badge variant="secondary" className="text-yellow-600">
当前仅允许保留白名单数量:5个
</Badge>
</div>
<DataTable<WhiteIpItem>
data={list}
status="done"
columns={[
{
accessorKey: "ip",
header: "IP地址",
},
{
accessorKey: "createTime",
header: "设置时间",
},
{
accessorKey: "isLocked",
header: "锁定状态",
cell: ({ row }) => {
const isLocked = row.original.isLocked === "已锁定"
return (
<Badge variant={isLocked ? "destructive" : "outline"}>
{row.original.isLocked}
</Badge>
)
},
},
{
id: "actions",
header: "操作",
cell: ({ row }) => {
const item = row.original
// TODO: 接入 lockWhiteIp / deleteWhiteIp
return (
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() =>
setList(prev =>
prev.map(i =>
i.id === item.id
? {
...i,
isLocked:
i.isLocked === "未锁定"
? "已锁定"
: "未锁定",
}
: i,
),
)
}
>
{item.isLocked === "未锁定" ? (
<Lock className="mr-1 h-3 w-3" />
) : (
<Unlock className="mr-1 h-3 w-3" />
)}
{item.isLocked === "未锁定" ? "锁定" : "解锁"}
</Button>
<Button
variant="destructive"
size="sm"
onClick={() =>
setList(prev => prev.filter(i => i.id !== item.id))
}
>
<Trash2 className="mr-1 h-3 w-3" />
删除
</Button>
</div>
)
},
},
]}
/>
</div>
</div>
</Page>
)
}