feat: rbac初步对接完成

This commit is contained in:
Blizzard
2026-04-30 22:53:46 +08:00
parent 3ed0b76fc2
commit e3e38800aa
45 changed files with 1637 additions and 467 deletions
+31 -30
View File
@@ -16,7 +16,7 @@ export default function Logs() {
const [logs, setLogs] = useState<OperationLog[]>([])
const [total, setTotal] = useState(0)
const [loading, setLoading] = useState(false)
const [search, setSearch] = useState({ operatorName: '', clientId: 'all', path: '' })
const [search, setSearch] = useState({ path: '', method: '', status: undefined as number | undefined })
const [pagination, setPagination] = useState({ current: 1, pageSize: 12 })
// Dialog State for viewing details
@@ -27,9 +27,9 @@ export default function Logs() {
setLoading(true)
try {
const res = await getLogList({ ...pagination, ...search })
if (res.data) {
setLogs(res.data.list)
setTotal(res.data.total)
if (res) {
setLogs(res.list)
setTotal(res.total)
}
} finally {
setLoading(false)
@@ -44,7 +44,7 @@ export default function Logs() {
}
const handleReset = () => {
setSearch({ operatorName: '', clientId: 'all', path: '' })
setSearch({ path: '', method: '', status: undefined })
if (pagination.current === 1) setTimeout(() => fetchLogs(), 0)
else setPagination({ ...pagination, current: 1 })
}
@@ -79,25 +79,27 @@ export default function Logs() {
</CardTitle>
<div className="flex flex-wrap items-center gap-3">
<Select value={search.clientId} onValueChange={v => setSearch({ ...search, clientId: v })}>
<Select value={search.method || 'all'} onValueChange={v => setSearch({ ...search, method: v === 'all' ? '' : v })}>
<SelectTrigger className="w-[140px] h-9">
<SelectValue placeholder="客户端来源" />
<SelectValue placeholder="请求方法" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
<SelectItem value="gateway">API </SelectItem>
<SelectItem value="plant">Plant </SelectItem>
<SelectItem value="radio">Radio </SelectItem>
<SelectItem value="all"></SelectItem>
<SelectItem value="GET">GET</SelectItem>
<SelectItem value="POST">POST</SelectItem>
<SelectItem value="PUT">PUT</SelectItem>
<SelectItem value="DELETE">DELETE</SelectItem>
</SelectContent>
</Select>
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="操作者账号..."
placeholder="状态码 (如 200, 400)..."
type="number"
className="pl-9 w-40 h-9"
value={search.operatorName}
onChange={e => setSearch({ ...search, operatorName: e.target.value })}
value={search.status || ''}
onChange={e => setSearch({ ...search, status: e.target.value ? Number(e.target.value) : undefined })}
onKeyDown={e => e.key === 'Enter' && handleSearch()}
/>
</div>
@@ -145,10 +147,10 @@ export default function Logs() {
) : (
logs.map(log => (
<TableRow key={log.id} className="hover:bg-muted/20">
<TableCell className="font-medium pl-6">{log.operatorName || '-'}</TableCell>
<TableCell className="font-medium pl-6">{log.userId || '-'}</TableCell>
<TableCell>
<Badge variant="outline" className="bg-primary/5 font-normal">
{log.clientName || log.clientId || 'System'}
{log.clientId || 'System'}
</Badge>
</TableCell>
<TableCell>
@@ -158,29 +160,28 @@ export default function Logs() {
</TableCell>
<TableCell>
<div className="flex flex-col gap-0.5">
<span className="font-medium text-sm">{log.title}</span>
<span className="text-xs text-muted-foreground font-mono">{log.path}</span>
<span className="font-medium text-sm text-muted-foreground font-mono">{log.path}</span>
</div>
</TableCell>
<TableCell>
{log.statusCode === 200 ? (
{log.status === 200 ? (
<div className="flex items-center text-emerald-600 text-sm">
<Activity className="w-4 h-4 mr-1" /> 200 OK
</div>
) : (
<div className="flex items-center text-red-500 text-sm font-medium">
<AlertCircle className="w-4 h-4 mr-1" /> {log.statusCode}
<AlertCircle className="w-4 h-4 mr-1" /> {log.status}
</div>
)}
</TableCell>
<TableCell>
<div className={`flex items-center text-sm font-mono ${log.duration > 200 ? 'text-amber-500 font-medium' : 'text-muted-foreground'}`}>
<div className={`flex items-center text-sm font-mono ${log.latency > 200000000 ? 'text-amber-500 font-medium' : 'text-muted-foreground'}`}>
<Clock className="w-3.5 h-3.5 mr-1.5 opacity-70" />
{log.duration}ms
{(log.latency / 1000000).toFixed(0)}ms
</div>
</TableCell>
<TableCell className="text-muted-foreground text-sm font-mono">
{new Date(log.createdAt).toLocaleString('zh-CN')}
{new Date(log.createdAt * 1000).toLocaleString('zh-CN')}
</TableCell>
<TableCell className="text-right pr-6">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-blue-500" onClick={() => openDetail(log)}>
@@ -232,29 +233,29 @@ export default function Logs() {
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4 p-4 rounded-lg bg-muted/30 border border-border/50 text-sm">
<div><span className="text-muted-foreground inline-block w-20">:</span> <span className="font-mono font-medium">{activeLog.path}</span></div>
<div><span className="text-muted-foreground inline-block w-20">:</span> <span className="font-medium">{activeLog.operatorName}</span></div>
<div><span className="text-muted-foreground inline-block w-20">:</span> {activeLog.clientName}</div>
<div><span className="text-muted-foreground inline-block w-20">ID:</span> <span className="font-medium">{activeLog.userId}</span></div>
<div><span className="text-muted-foreground inline-block w-20">:</span> {activeLog.clientId}</div>
<div><span className="text-muted-foreground inline-block w-20">IP地址:</span> <span className="font-mono">{activeLog.ip}</span></div>
<div className="col-span-2"><span className="text-muted-foreground inline-block w-20">User-Agent:</span> <span className="text-xs text-muted-foreground">{activeLog.userAgent}</span></div>
<div className="col-span-2"><span className="text-muted-foreground inline-block w-20">User-Agent:</span> <span className="text-xs text-muted-foreground">{activeLog.agent}</span></div>
</div>
{activeLog.requestBody && (
{activeLog.body && (
<div className="space-y-2">
<h4 className="text-sm font-semibold flex items-center gap-2"><Activity className="w-4 h-4" /> Request Payload</h4>
<ScrollArea className="h-[120px] w-full rounded-md border bg-zinc-950 p-4">
<pre className="text-xs text-emerald-400 font-mono leading-relaxed">
{JSON.stringify(JSON.parse(activeLog.requestBody), null, 2)}
{JSON.stringify(JSON.parse(activeLog.body), null, 2)}
</pre>
</ScrollArea>
</div>
)}
{activeLog.responseBody && (
{activeLog.resp && (
<div className="space-y-2">
<h4 className="text-sm font-semibold flex items-center gap-2"><Activity className="w-4 h-4" /> Response Data</h4>
<ScrollArea className="h-[150px] w-full rounded-md border bg-zinc-950 p-4">
<pre className="text-xs text-blue-400 font-mono leading-relaxed">
{JSON.stringify(JSON.parse(activeLog.responseBody), null, 2)}
{JSON.stringify(JSON.parse(activeLog.resp), null, 2)}
</pre>
</ScrollArea>
</div>