From 2a63f97f354284bb95bfb0ae65cdb3a0f7b13364 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 2 Jul 2026 15:39:03 +0800 Subject: [PATCH] refactor: standardize admin page shell --- frontend/src/components/app/AppPage.tsx | 25 +++++++++++++++++++ .../design-system/DesignSystemPreview.tsx | 7 +++--- .../features/solutions/SolutionCreatePage.tsx | 7 +++--- .../features/solutions/SolutionListPage.tsx | 7 +++--- frontend/src/features/system/AuditLogPage.tsx | 7 +++--- .../src/features/system/SystemMenusPage.tsx | 7 +++--- .../src/features/system/SystemRolesPage.tsx | 7 +++--- .../features/system/SystemUserGroupsPage.tsx | 7 +++--- .../src/features/system/SystemUsersPage.tsx | 7 +++--- .../src/features/tickets/TicketCreatePage.tsx | 7 +++--- .../src/features/tickets/TicketDetailPage.tsx | 15 ++++++----- .../src/features/tickets/TicketListPage.tsx | 7 +++--- .../src/features/workbench/WorkbenchPage.tsx | 7 +++--- 13 files changed, 65 insertions(+), 52 deletions(-) create mode 100644 frontend/src/components/app/AppPage.tsx diff --git a/frontend/src/components/app/AppPage.tsx b/frontend/src/components/app/AppPage.tsx new file mode 100644 index 0000000..96ec379 --- /dev/null +++ b/frontend/src/components/app/AppPage.tsx @@ -0,0 +1,25 @@ +import type * as React from 'react' + +import { cn } from '@/lib/utils' + +type AppPageProps = { + children: React.ReactNode + className?: string + contentClassName?: string + maxWidth?: '6xl' | '7xl' +} + +const maxWidthClass: Record, string> = { + '6xl': 'max-w-6xl', + '7xl': 'max-w-7xl' +} + +export function AppPage({ children, className, contentClassName, maxWidth = '7xl' }: AppPageProps) { + return ( +
+
+ {children} +
+
+ ) +} diff --git a/frontend/src/features/design-system/DesignSystemPreview.tsx b/frontend/src/features/design-system/DesignSystemPreview.tsx index 160e2cb..b43c922 100644 --- a/frontend/src/features/design-system/DesignSystemPreview.tsx +++ b/frontend/src/features/design-system/DesignSystemPreview.tsx @@ -2,6 +2,7 @@ import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataT import { AppDetailLayout } from '@/components/app/AppDetailLayout' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppFilterBar } from '@/components/app/AppFilterBar' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' import { AppSection } from '@/components/app/AppSection' @@ -33,8 +34,7 @@ const previewColumns: AppDataTableColumn[] = [ export function DesignSystemPreview() { return ( -
-
+ 创建工单} @@ -102,7 +102,6 @@ export function DesignSystemPreview() {

页面级布局只复用 App 组件,业务字段由页面自行传入。

-
-
+ ) } diff --git a/frontend/src/features/solutions/SolutionCreatePage.tsx b/frontend/src/features/solutions/SolutionCreatePage.tsx index 1d9b938..d2ca6d5 100644 --- a/frontend/src/features/solutions/SolutionCreatePage.tsx +++ b/frontend/src/features/solutions/SolutionCreatePage.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { ArrowLeft, Save } from 'lucide-react' import { AppField } from '@/components/app/AppField' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppSection } from '@/components/app/AppSection' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' @@ -77,8 +78,7 @@ export function SolutionCreatePage() { } return ( -
-
+
- -
+ ) } diff --git a/frontend/src/features/solutions/SolutionListPage.tsx b/frontend/src/features/solutions/SolutionListPage.tsx index dcc824b..fdf8d62 100644 --- a/frontend/src/features/solutions/SolutionListPage.tsx +++ b/frontend/src/features/solutions/SolutionListPage.tsx @@ -4,6 +4,7 @@ import { Plus } from 'lucide-react' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppFilterBar } from '@/components/app/AppFilterBar' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Card, CardContent } from '@/components/ui/card' @@ -73,8 +74,7 @@ export function SolutionListPage() { : solutions return ( -
-
+ 当前显示 {visibleSolutions.length} 条方案

} /> ) : null} -
-
+ ) } diff --git a/frontend/src/features/system/AuditLogPage.tsx b/frontend/src/features/system/AuditLogPage.tsx index 897b98c..51de02f 100644 --- a/frontend/src/features/system/AuditLogPage.tsx +++ b/frontend/src/features/system/AuditLogPage.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppFilterBar } from '@/components/app/AppFilterBar' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' @@ -65,8 +66,7 @@ export function AuditLogPage() { ] return ( -
-
+ @@ -83,7 +83,6 @@ export function AuditLogPage() { footer={

当前显示 {filteredLogs.length} 条记录

} /> ) : null} -
-
+ ) } diff --git a/frontend/src/features/system/SystemMenusPage.tsx b/frontend/src/features/system/SystemMenusPage.tsx index 73fe7df..ff57e0e 100644 --- a/frontend/src/features/system/SystemMenusPage.tsx +++ b/frontend/src/features/system/SystemMenusPage.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' @@ -78,8 +79,7 @@ export function SystemMenusPage() { ] return ( -
-
+
@@ -96,7 +96,6 @@ export function SystemMenusPage() { footer={

当前显示 {filteredMenus.length} 个菜单项

} /> ) : null} -
-
+ ) } diff --git a/frontend/src/features/system/SystemRolesPage.tsx b/frontend/src/features/system/SystemRolesPage.tsx index 745d652..339632a 100644 --- a/frontend/src/features/system/SystemRolesPage.tsx +++ b/frontend/src/features/system/SystemRolesPage.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import { AppField } from '@/components/app/AppField' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -71,8 +72,7 @@ export function SystemRolesPage() { ] return ( -
-
+
@@ -98,8 +98,7 @@ export function SystemRolesPage() { onClose={() => setEditingRole(null)} onSaved={load} /> -
-
+ ) } diff --git a/frontend/src/features/system/SystemUserGroupsPage.tsx b/frontend/src/features/system/SystemUserGroupsPage.tsx index 8f3ea5b..cb00e67 100644 --- a/frontend/src/features/system/SystemUserGroupsPage.tsx +++ b/frontend/src/features/system/SystemUserGroupsPage.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import { AppField } from '@/components/app/AppField' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -79,8 +80,7 @@ export function SystemUserGroupsPage() { ] return ( -
-
+ @@ -106,7 +106,6 @@ export function SystemUserGroupsPage() { footer={

共 {groups.length} 个用户组

} /> ) : null} -
-
+ ) } diff --git a/frontend/src/features/system/SystemUsersPage.tsx b/frontend/src/features/system/SystemUsersPage.tsx index dc6d38e..86694a5 100644 --- a/frontend/src/features/system/SystemUsersPage.tsx +++ b/frontend/src/features/system/SystemUsersPage.tsx @@ -4,6 +4,7 @@ import { AppField } from '@/components/app/AppField' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppFilterBar } from '@/components/app/AppFilterBar' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -138,8 +139,7 @@ export function SystemUsersPage() { ] return ( -
-
+ @@ -180,8 +180,7 @@ export function SystemUsersPage() { onClose={() => setEditingUser(null)} onSaved={load} /> -
-
+ ) } diff --git a/frontend/src/features/tickets/TicketCreatePage.tsx b/frontend/src/features/tickets/TicketCreatePage.tsx index dc4bfa9..b0bd32a 100644 --- a/frontend/src/features/tickets/TicketCreatePage.tsx +++ b/frontend/src/features/tickets/TicketCreatePage.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { AlertCircle, Bot, CheckCircle2, Lightbulb, Send } from 'lucide-react' import { AppField } from '@/components/app/AppField' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppSection } from '@/components/app/AppSection' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' @@ -47,8 +48,7 @@ export function TicketCreatePage() { } return ( -
-
+
@@ -175,7 +175,6 @@ export function TicketCreatePage() {
-
-
+ ) } diff --git a/frontend/src/features/tickets/TicketDetailPage.tsx b/frontend/src/features/tickets/TicketDetailPage.tsx index 64e43eb..124fe3a 100644 --- a/frontend/src/features/tickets/TicketDetailPage.tsx +++ b/frontend/src/features/tickets/TicketDetailPage.tsx @@ -3,6 +3,7 @@ import { Bot, CheckCircle2, CircleDot, Send, XCircle } from 'lucide-react' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppDetailLayout } from '@/components/app/AppDetailLayout' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' import { AppSection } from '@/components/app/AppSection' @@ -87,23 +88,22 @@ export function TicketDetailPage({ ticketId }: { ticketId: number }) { if (error) { return ( -
+ -
+ ) } if (!ticket) { return ( -
+ -
+ ) } return ( -
-
+ -
-
+ ) } diff --git a/frontend/src/features/tickets/TicketListPage.tsx b/frontend/src/features/tickets/TicketListPage.tsx index 55a2190..b205694 100644 --- a/frontend/src/features/tickets/TicketListPage.tsx +++ b/frontend/src/features/tickets/TicketListPage.tsx @@ -3,6 +3,7 @@ import { ArrowRight, Plus } from 'lucide-react' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' import { AppStatusBadge } from '@/components/app/AppStatusBadge' @@ -194,8 +195,7 @@ export function TicketListPage() { } return ( -
-
+ (window.location.hash = `/tickets/${ticket.id}`)} /> ) : null} -
-
+ ) } diff --git a/frontend/src/features/workbench/WorkbenchPage.tsx b/frontend/src/features/workbench/WorkbenchPage.tsx index 133487a..9e8b0b2 100644 --- a/frontend/src/features/workbench/WorkbenchPage.tsx +++ b/frontend/src/features/workbench/WorkbenchPage.tsx @@ -3,6 +3,7 @@ import { ArrowRight, Plus } from 'lucide-react' import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' import { AppSection } from '@/components/app/AppSection' @@ -61,8 +62,7 @@ export function WorkbenchPage() { const recentTickets = tickets.slice(0, 6) return ( -
-
+
- -
+ ) }