Files
users/components/dialogs/api-key-management-dialog.tsx
v0 b17b488f8e refactor: restructure navigation and module layout
Reorganize navigation and module structure based on new requirements.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
2026-01-31 04:32:36 +00:00

165 lines
5.5 KiB
TypeScript

"use client"
import { useState } from "react"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Badge } from "@/components/ui/badge"
import { Key, Copy, Eye, EyeOff, Plus, Trash2, CheckCircle } from "lucide-react"
interface ApiKeyManagementDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
}
const MOCK_KEYS = [
{
id: 1,
name: "生产环境密钥",
key: "sk_live_xxxxxxxxxxxx",
created: "2024-12-01",
lastUsed: "刚刚",
status: "active",
},
{
id: 2,
name: "测试环境密钥",
key: "sk_test_xxxxxxxxxxxx",
created: "2024-11-15",
lastUsed: "3天前",
status: "active",
},
{
id: 3,
name: "开发调试密钥",
key: "sk_dev_xxxxxxxxxxxx",
created: "2024-10-20",
lastUsed: "1周前",
status: "inactive",
},
]
export function ApiKeyManagementDialog({ open, onOpenChange }: ApiKeyManagementDialogProps) {
const [keys, setKeys] = useState(MOCK_KEYS)
const [showKey, setShowKey] = useState<number | null>(null)
const [newKeyName, setNewKeyName] = useState("")
const [showNewKeyForm, setShowNewKeyForm] = useState(false)
const [copiedId, setCopiedId] = useState<number | null>(null)
const copyKey = (id: number, key: string) => {
navigator.clipboard.writeText(key)
setCopiedId(id)
setTimeout(() => setCopiedId(null), 2000)
}
const createKey = () => {
if (newKeyName) {
setKeys([
...keys,
{
id: Date.now(),
name: newKeyName,
key: `sk_live_${Math.random().toString(36).substr(2, 12)}`,
created: new Date().toISOString().split("T")[0],
lastUsed: "从未",
status: "active",
},
])
setNewKeyName("")
setShowNewKeyForm(false)
}
}
const deleteKey = (id: number) => {
setKeys(keys.filter((k) => k.id !== id))
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Key className="w-5 h-5" />
API密钥管理
</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="flex justify-end">
<Button size="sm" onClick={() => setShowNewKeyForm(true)}>
<Plus className="w-4 h-4 mr-1" />
</Button>
</div>
{showNewKeyForm && (
<div className="p-4 border rounded-lg bg-gray-50 space-y-3">
<Label></Label>
<div className="flex gap-2">
<Input
placeholder="例如:生产环境密钥"
value={newKeyName}
onChange={(e) => setNewKeyName(e.target.value)}
/>
<Button onClick={createKey}></Button>
<Button variant="outline" onClick={() => setShowNewKeyForm(false)}>
</Button>
</div>
</div>
)}
<div className="space-y-3">
{keys.map((item) => (
<div key={item.id} className="p-4 border rounded-lg hover:bg-gray-50">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="font-medium">{item.name}</span>
<Badge
className={item.status === "active" ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-700"}
>
{item.status === "active" ? "启用" : "停用"}
</Badge>
</div>
<Button
variant="ghost"
size="sm"
className="text-red-500 hover:text-red-700"
onClick={() => deleteKey(item.id)}
>
<Trash2 className="w-4 h-4" />
</Button>
</div>
<div className="flex items-center gap-2 mb-2">
<code className="flex-1 px-3 py-2 bg-gray-100 rounded text-sm font-mono">
{showKey === item.id ? item.key : item.key.replace(/./g, "•").slice(0, 20) + "..."}
</code>
<Button variant="ghost" size="sm" onClick={() => setShowKey(showKey === item.id ? null : item.id)}>
{showKey === item.id ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</Button>
<Button variant="ghost" size="sm" onClick={() => copyKey(item.id, item.key)}>
{copiedId === item.id ? (
<CheckCircle className="w-4 h-4 text-green-500" />
) : (
<Copy className="w-4 h-4" />
)}
</Button>
</div>
<div className="flex items-center gap-4 text-xs text-gray-500">
<span> {item.created}</span>
<span>使: {item.lastUsed}</span>
</div>
</div>
))}
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}