Files
ai-ticket-platform/frontend/src/features/system/SystemMenusPage.tsx
2026-07-01 14:19:59 +08:00

104 lines
3.4 KiB
TypeScript

import * as React from 'react'
import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable'
import { AppEmptyState } from '@/components/app/AppEmptyState'
import { AppPageHeader } from '@/components/app/AppPageHeader'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'
import { listMenus, updateMenu } from './api'
import type { SystemMenu } from './types'
export function SystemMenusPage() {
const [menus, setMenus] = React.useState<SystemMenu[]>([])
const [query, setQuery] = React.useState('')
const [error, setError] = React.useState('')
const load = React.useCallback(async () => {
try {
setMenus(await listMenus())
setError('')
} catch (reason) {
setError(reason instanceof Error ? reason.message : '加载失败')
}
}, [])
React.useEffect(() => {
void load()
}, [load])
const filteredMenus = menus.filter((menu) => {
const keyword = query.trim().toLowerCase()
if (!keyword) return true
return `${menu.code} ${menu.title} ${menu.path} ${menu.permission_code}`.toLowerCase().includes(keyword)
})
async function toggleVisible(menu: SystemMenu, checked: boolean) {
await updateMenu(menu.id, { is_visible: checked })
await load()
}
async function toggleEnabled(menu: SystemMenu, checked: boolean) {
await updateMenu(menu.id, { is_enabled: checked })
await load()
}
const columns: AppDataTableColumn<SystemMenu>[] = [
{
key: 'title',
header: '菜单',
cell: (menu) => (
<div>
<p className="font-medium text-foreground">{menu.title}</p>
<p className="mt-1 text-sm text-muted-foreground">{menu.code}</p>
</div>
)
},
{
key: 'path',
header: '路径',
cell: (menu) => <span className="text-muted-foreground">{menu.path || '-'}</span>
},
{
key: 'permission',
header: '权限码',
cell: (menu) => menu.permission_code ? <Badge variant="secondary">{menu.permission_code}</Badge> : '-'
},
{
key: 'visible',
header: '展示',
cell: (menu) => <Switch checked={menu.is_visible} onCheckedChange={(checked) => void toggleVisible(menu, checked)} />
},
{
key: 'enabled',
header: '启用',
cell: (menu) => <Switch checked={menu.is_enabled} onCheckedChange={(checked) => void toggleEnabled(menu, checked)} />
}
]
return (
<main className="min-h-screen bg-background p-6 text-foreground">
<div className="mx-auto flex max-w-7xl flex-col gap-4">
<AppPageHeader title="菜单权限" description="按后端菜单配置控制前端导航展示和可用状态。" />
<div className="rounded-lg border border-border bg-surface p-3">
<Input placeholder="搜索菜单、路径或权限码" value={query} onChange={(event) => setQuery(event.target.value)} />
</div>
{error ? <AppEmptyState title="无法加载菜单" description={error} /> : null}
{!error ? (
<AppDataTable
columns={columns}
rows={filteredMenus}
getRowKey={(menu) => menu.id}
emptyTitle="暂无菜单"
emptyDescription="执行治理初始化后,系统菜单会显示在这里。"
footer={<p className="text-sm text-muted-foreground">当前显示 {filteredMenus.length} 个菜单项</p>}
/>
) : null}
</div>
</main>
)
}