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>
This commit is contained in:
164
components/dialogs/api-key-management-dialog.tsx
Normal file
164
components/dialogs/api-key-management-dialog.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
"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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user