refactor: tighten shadcn admin ui polish
This commit is contained in:
@@ -3,7 +3,7 @@ import { AlertCircle, LockKeyhole, UserRound } from 'lucide-react'
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
|
||||
@@ -60,7 +60,6 @@ export function LoginPage() {
|
||||
<Card className="w-full max-w-[400px]">
|
||||
<CardHeader>
|
||||
<CardTitle>登录平台</CardTitle>
|
||||
<CardDescription>使用管理员账号进入企业后台</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form className="grid gap-4" onSubmit={handleSubmit}>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AppSection } from '@/components/app/AppSection'
|
||||
import { AppStatusBadge } from '@/components/app/AppStatusBadge'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
|
||||
@@ -37,13 +37,12 @@ export function DesignSystemPreview() {
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-6">
|
||||
<AppPageHeader
|
||||
title="设计系统预览"
|
||||
description="业务页面必须基于 shadcn 原子组件和 App 组件层开发。"
|
||||
actions={<Button>创建工单</Button>}
|
||||
/>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h2 className="text-sm font-medium">按钮</h2>
|
||||
<CardTitle className="text-sm">按钮</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<Button>主要操作</Button>
|
||||
@@ -55,7 +54,7 @@ export function DesignSystemPreview() {
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h2 className="text-sm font-medium">状态与优先级</h2>
|
||||
<CardTitle className="text-sm">状态与优先级</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<Badge>默认</Badge>
|
||||
@@ -88,17 +87,16 @@ export function DesignSystemPreview() {
|
||||
rows={previewRows}
|
||||
getRowKey={(row) => row.id}
|
||||
emptyTitle="暂无数据"
|
||||
emptyDescription="符合条件的数据会显示在这里。"
|
||||
/>
|
||||
|
||||
<AppDetailLayout
|
||||
side={
|
||||
<AppSection title="右侧面板" description="用于关键字段、关联方案和 AI 分析。">
|
||||
<AppEmptyState title="等待接入" description="后续阶段会接入 Agent 执行结果。" />
|
||||
<AppSection title="右侧面板">
|
||||
<AppEmptyState title="等待接入" />
|
||||
</AppSection>
|
||||
}
|
||||
>
|
||||
<AppSection title="详情区块" description="用于承载工单概览、问题描述和处理动态。">
|
||||
<AppSection title="详情区块">
|
||||
<p className="text-sm text-muted-foreground">页面级布局只复用 App 组件,业务字段由页面自行传入。</p>
|
||||
</AppSection>
|
||||
</AppDetailLayout>
|
||||
|
||||
@@ -79,7 +79,7 @@ export function SolutionListPage() {
|
||||
title="解决方案库"
|
||||
actions={
|
||||
<Button onClick={() => (window.location.hash = '/solutions/new')}>
|
||||
<Plus className="size-4" />
|
||||
<Plus data-icon="inline-start" />
|
||||
新建方案
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function AuditLogPage() {
|
||||
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="查看平台关键操作记录,辅助权限治理和问题追踪。" />
|
||||
<AppPageHeader title="操作审计" />
|
||||
|
||||
<AppFilterBar>
|
||||
<Input placeholder="搜索模块、动作、对象或结果" value={query} onChange={(event) => setQuery(event.target.value)} />
|
||||
@@ -80,7 +80,6 @@ export function AuditLogPage() {
|
||||
rows={filteredLogs}
|
||||
getRowKey={(log) => log.id}
|
||||
emptyTitle="暂无审计日志"
|
||||
emptyDescription="产生关键操作后,审计记录会显示在这里。"
|
||||
footer={<p className="text-sm text-muted-foreground">当前显示 {filteredLogs.length} 条记录</p>}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -80,7 +80,7 @@ export function SystemMenusPage() {
|
||||
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="按后端菜单配置控制前端导航展示和可用状态。" />
|
||||
<AppPageHeader title="菜单权限" />
|
||||
|
||||
<div className="rounded-lg border border-border bg-surface p-3">
|
||||
<Input placeholder="搜索菜单、路径或权限码" value={query} onChange={(event) => setQuery(event.target.value)} />
|
||||
@@ -93,7 +93,6 @@ export function SystemMenusPage() {
|
||||
rows={filteredMenus}
|
||||
getRowKey={(menu) => menu.id}
|
||||
emptyTitle="暂无菜单"
|
||||
emptyDescription="执行治理初始化后,系统菜单会显示在这里。"
|
||||
footer={<p className="text-sm text-muted-foreground">当前显示 {filteredMenus.length} 个菜单项</p>}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AppPageHeader } from '@/components/app/AppPageHeader'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
import { createRole, listPermissions, listRoles, setRolePermissions } from './api'
|
||||
@@ -72,7 +72,7 @@ export function SystemRolesPage() {
|
||||
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="定义后台角色,并将系统权限授予对应角色。" />
|
||||
<AppPageHeader title="角色权限" />
|
||||
|
||||
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[minmax(0,360px)_auto]" onSubmit={handleCreate}>
|
||||
<Input placeholder="角色名称" value={roleName} onChange={(event) => setRoleName(event.target.value)} required />
|
||||
@@ -86,7 +86,6 @@ export function SystemRolesPage() {
|
||||
rows={roles}
|
||||
getRowKey={(role) => role.id}
|
||||
emptyTitle="暂无角色"
|
||||
emptyDescription="创建角色后可继续配置权限。"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -137,8 +136,7 @@ function RolePermissionDialog({
|
||||
<Dialog open={Boolean(role)} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="sm:max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>配置角色权限</DialogTitle>
|
||||
<DialogDescription>{role?.name} 可访问的菜单和接口能力。</DialogDescription>
|
||||
<DialogTitle>配置角色权限{role ? `:${role.name}` : ''}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid max-h-[56vh] gap-4 overflow-auto pr-1">
|
||||
{Object.entries(groupedPermissions).map(([module, modulePermissions]) => (
|
||||
|
||||
@@ -80,7 +80,7 @@ export function SystemUserGroupsPage() {
|
||||
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="按业务团队、系统域或支持队列组织用户。" />
|
||||
<AppPageHeader title="用户组管理" />
|
||||
|
||||
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_2fr_auto]" onSubmit={handleCreate}>
|
||||
<Input placeholder="用户组名称" value={form.name} onChange={(event) => setForm({ ...form, name: event.target.value })} required />
|
||||
@@ -96,7 +96,6 @@ export function SystemUserGroupsPage() {
|
||||
rows={groups}
|
||||
getRowKey={(group) => group.id}
|
||||
emptyTitle="暂无用户组"
|
||||
emptyDescription="创建用户组后会显示在这里。"
|
||||
footer={<p className="text-sm text-muted-foreground">共 {groups.length} 个用户组</p>}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AppPageHeader } from '@/components/app/AppPageHeader'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
@@ -139,7 +139,7 @@ export function SystemUsersPage() {
|
||||
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="维护平台账号、启用状态、角色和业务用户组。" />
|
||||
<AppPageHeader title="用户管理" />
|
||||
|
||||
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_1fr_1fr_auto]" onSubmit={handleCreateUser}>
|
||||
<Input placeholder="账号" value={form.username} onChange={(event) => setForm({ ...form, username: event.target.value })} required />
|
||||
@@ -160,7 +160,6 @@ export function SystemUsersPage() {
|
||||
rows={filteredUsers}
|
||||
getRowKey={(user) => user.id}
|
||||
emptyTitle="暂无用户"
|
||||
emptyDescription="创建账号后会显示在这里。"
|
||||
footer={<p className="text-sm text-muted-foreground">当前显示 {filteredUsers.length} 个用户</p>}
|
||||
/>
|
||||
) : null}
|
||||
@@ -209,8 +208,7 @@ function UserAccessDialog({
|
||||
<Dialog open={Boolean(user)} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>用户授权</DialogTitle>
|
||||
<DialogDescription>为 {user?.display_name || user?.username} 分配角色和用户组。</DialogDescription>
|
||||
<DialogTitle>用户授权{user ? `:${user.display_name || user.username}` : ''}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
|
||||
@@ -133,7 +133,7 @@ export function TicketCreatePage() {
|
||||
<span>请确认标题和问题描述已足够支持技术人员开始排查。</span>
|
||||
</div>
|
||||
<Button type="submit" disabled={submitting}>
|
||||
<Send className="size-4" />
|
||||
<Send data-icon="inline-start" />
|
||||
{submitting ? '提交中...' : '提交工单'}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -160,7 +160,7 @@ export function TicketCreatePage() {
|
||||
|
||||
<AppSection
|
||||
title="AI 建单辅助"
|
||||
action={<Badge variant="ai"><Bot className="size-3" /> 即将接入</Badge>}
|
||||
action={<Badge variant="ai">即将接入</Badge>}
|
||||
>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
后续阶段将接入建单 Agent,用于补全字段、追问缺失信息和推荐相似问题。
|
||||
|
||||
@@ -124,7 +124,7 @@ export function TicketDetailPage({ ticketId }: { ticketId: number }) {
|
||||
variant={status === 'closed' ? 'outline' : 'secondary'}
|
||||
onClick={() => openTransition(status)}
|
||||
>
|
||||
{Icon ? <Icon className="size-4" /> : null}
|
||||
{Icon ? <Icon data-icon="inline-start" /> : null}
|
||||
{getStatusActionLabel(ticket.status, status)}
|
||||
</Button>
|
||||
)
|
||||
|
||||
@@ -113,7 +113,7 @@ const columns: AppDataTableColumn<Ticket>[] = [
|
||||
}}
|
||||
>
|
||||
详情
|
||||
<ArrowRight className="size-4" />
|
||||
<ArrowRight data-icon="inline-end" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -200,7 +200,7 @@ export function TicketListPage() {
|
||||
title="工单中心"
|
||||
actions={
|
||||
<Button onClick={() => (window.location.hash = '/tickets/new')}>
|
||||
<Plus className="size-4" />
|
||||
<Plus data-icon="inline-start" />
|
||||
新建工单
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -65,10 +65,9 @@ export function WorkbenchPage() {
|
||||
<div className="mx-auto grid max-w-7xl gap-5">
|
||||
<AppPageHeader
|
||||
title="今日工作台"
|
||||
description="集中查看待处理事项、近期工单和常用入口。"
|
||||
actions={
|
||||
<Button onClick={() => (window.location.hash = '/tickets/new')}>
|
||||
<Plus className="size-4" />
|
||||
<Plus data-icon="inline-start" />
|
||||
新建工单
|
||||
</Button>
|
||||
}
|
||||
@@ -90,11 +89,10 @@ export function WorkbenchPage() {
|
||||
<div className="grid gap-4 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<AppSection
|
||||
title="最近工单"
|
||||
description="按当前接口返回顺序展示近期工单,后续可接入专门的工作台聚合接口。"
|
||||
action={
|
||||
<Button variant="ghost" onClick={() => (window.location.hash = '/tickets')}>
|
||||
查看全部
|
||||
<ArrowRight className="size-4" />
|
||||
<ArrowRight data-icon="inline-end" />
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
@@ -108,19 +106,19 @@ export function WorkbenchPage() {
|
||||
/>
|
||||
</AppSection>
|
||||
|
||||
<AppSection title="快捷入口" description="高频操作集中放置,减少跨页面跳转。">
|
||||
<AppSection title="快捷入口">
|
||||
<div className="grid gap-2">
|
||||
<Button className="justify-between" variant="secondary" onClick={() => (window.location.hash = '/tickets/new')}>
|
||||
创建工单
|
||||
<ArrowRight className="size-4" />
|
||||
<ArrowRight data-icon="inline-end" />
|
||||
</Button>
|
||||
<Button className="justify-between" variant="outline" onClick={() => (window.location.hash = '/tickets')}>
|
||||
工单中心
|
||||
<ArrowRight className="size-4" />
|
||||
<ArrowRight data-icon="inline-end" />
|
||||
</Button>
|
||||
<Button className="justify-between" variant="outline" onClick={() => (window.location.hash = '/solutions')}>
|
||||
解决方案库
|
||||
<ArrowRight className="size-4" />
|
||||
<ArrowRight data-icon="inline-end" />
|
||||
</Button>
|
||||
</div>
|
||||
</AppSection>
|
||||
|
||||
Reference in New Issue
Block a user