From aca50b95118d3722314f6217aed74d96a77a0490 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Sat, 23 May 2026 23:16:01 +0800 Subject: [PATCH] feat: refactor dashboard components to use new layout structure and improve UI consistency --- web/app/dashboard/channels/page.tsx | 86 +++++++----- web/app/dashboard/companies/page.tsx | 114 ++++++++-------- web/app/dashboard/permissions/page.tsx | 127 ++++++++---------- web/app/dashboard/quick-replies/page.tsx | 161 +++++++++++------------ web/app/dashboard/roles/page.tsx | 88 ++++++++----- web/app/dashboard/tags/page.tsx | 68 ++++++---- web/components/dashboard-page.tsx | 121 +++++++++++++++++ 7 files changed, 454 insertions(+), 311 deletions(-) create mode 100644 web/components/dashboard-page.tsx diff --git a/web/app/dashboard/channels/page.tsx b/web/app/dashboard/channels/page.tsx index 23d8cd0..1622f32 100644 --- a/web/app/dashboard/channels/page.tsx +++ b/web/app/dashboard/channels/page.tsx @@ -23,6 +23,12 @@ import { type CreateAdminChannelPayload, type PageResult, } from "@/lib/api/admin" +import { + DashboardPage, + DashboardTableShell, + DashboardTableStateRow, + DashboardToolbar, +} from "@/components/dashboard-page" import { OptionCombobox } from "@/components/option-combobox" import { EditDialog } from "./_components/edit" import { Badge } from "@/components/ui/badge" @@ -205,16 +211,29 @@ export default function DashboardChannelsPage() { return ( <> -
-
+ + + + + + } + > setNameInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按渠道名称筛选" - className="w-full xl:w-56" + className="w-full sm:w-56" /> -
+
-
+
-
+
查询 - - -
+ -
+ setPage(nextPage)} + onLimitChange={(nextLimit) => { + setLimit(nextLimit) + setPage(1) + }} + /> + } + > @@ -271,12 +296,13 @@ export default function DashboardChannelsPage() { - {result.results.length === 0 ? ( - - - {loading ? "正在加载接入渠道..." : "暂无接入渠道"} - - + {loading || result.results.length === 0 ? ( + ) : null} {result.results.map((item) => ( @@ -338,20 +364,8 @@ export default function DashboardChannelsPage() { ))}
-
- setPage(nextPage)} - onLimitChange={(nextLimit) => { - setLimit(nextLimit) - setPage(1) - }} - /> -
-
-
+ + -) { - return options.find((item) => item.value === value)?.label ?? "请选择状态" -} - export default function DashboardCompaniesPage() { const [nameInput, setNameInput] = useState("") const [codeInput, setCodeInput] = useState("") @@ -194,9 +187,22 @@ export default function DashboardCompaniesPage() { return ( <> -
-
-
+ + + + + + } + > +
setCodeInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按公司编码筛选" - className="w-full xl:w-48" + className="w-full sm:w-44" /> - +
+ +
- -
+
-
+ { + setLimit(nextLimit) + setPage(1) + }} + /> + } + > @@ -249,12 +261,13 @@ export default function DashboardCompaniesPage() { - {result.results.length === 0 && !loading ? ( - - - 暂无公司数据 - - + {loading || result.results.length === 0 ? ( + ) : ( result.results.map((item) => { const actionLoading = actionLoadingId === item.id @@ -331,20 +344,8 @@ export default function DashboardCompaniesPage() { )}
-
- - { - setLimit(nextLimit) - setPage(1) - }} - /> -
+ + ) } - diff --git a/web/app/dashboard/permissions/page.tsx b/web/app/dashboard/permissions/page.tsx index bdec0bb..d778876 100644 --- a/web/app/dashboard/permissions/page.tsx +++ b/web/app/dashboard/permissions/page.tsx @@ -5,22 +5,16 @@ import { KeyRoundIcon, RefreshCwIcon, RouteIcon, SearchIcon } from "lucide-react import { toast } from "sonner" import { - fetchPermissions, - type AdminPermission, - type PageResult, -} from "@/lib/api/admin" -import { Status, StatusLabels } from "@/lib/generated/enums" + DashboardPage, + DashboardTableShell, + DashboardTableStateRow, + DashboardToolbar, +} from "@/components/dashboard-page" +import { ListPagination } from "@/components/list-pagination" +import { OptionCombobox } from "@/components/option-combobox" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { ListPagination } from "@/components/list-pagination" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" import { Table, TableBody, @@ -29,6 +23,12 @@ import { TableHeader, TableRow, } from "@/components/ui/table" +import { + fetchPermissions, + type AdminPermission, + type PageResult, +} from "@/lib/api/admin" +import { Status, StatusLabels } from "@/lib/generated/enums" const listStatusOptions = [ { value: "all", label: "全部状态" }, @@ -37,13 +37,6 @@ const listStatusOptions = [ { value: String(Status.Deleted), label: StatusLabels[Status.Deleted] }, ] as const -function getStatusLabel( - value: string, - options: ReadonlyArray<{ value: string; label: string }> -) { - return options.find((item) => item.value === value)?.label ?? "请选择状态" -} - export default function DashboardPermissionsPage() { const [keywordInput, setKeywordInput] = useState("") const [groupNameInput, setGroupNameInput] = useState("") @@ -81,10 +74,6 @@ export default function DashboardPermissionsPage() { void loadData() }, [loadData]) - function handleStatusFilterChange(value: string | null) { - setStatusFilterInput(value ?? "all") - } - function applyFilters() { setKeyword(keywordInput) setGroupName(groupNameInput) @@ -109,9 +98,16 @@ export default function DashboardPermissionsPage() { } return ( -
-
-
+ + void loadData()} disabled={loading}> + + 刷新 + + } + > +
setGroupNameInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按分组筛选" - className="w-full xl:w-48" + className="w-full sm:w-44" /> - +
+ +
- -
-
-
+ + { + setLimit(nextLimit) + setPage(1) + }} + /> + } + > @@ -202,28 +200,17 @@ export default function DashboardPermissionsPage() { ))} - {!loading && result.results.length === 0 ? ( - - - 没有匹配的权限数据 - - + {loading || result.results.length === 0 ? ( + ) : null}
-
- { - setLimit(nextLimit) - setPage(1) - }} - /> -
-
+ + ) } diff --git a/web/app/dashboard/quick-replies/page.tsx b/web/app/dashboard/quick-replies/page.tsx index 2aee8fc..67b1ed1 100644 --- a/web/app/dashboard/quick-replies/page.tsx +++ b/web/app/dashboard/quick-replies/page.tsx @@ -11,6 +11,32 @@ import { } from "lucide-react" import { toast } from "sonner" +import { + DashboardPage, + DashboardTableShell, + DashboardTableStateRow, + DashboardToolbar, +} from "@/components/dashboard-page" +import { ListPagination } from "@/components/list-pagination" +import { OptionCombobox } from "@/components/option-combobox" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { ButtonGroup } from "@/components/ui/button-group" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { Input } from "@/components/ui/input" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" import { createQuickReply, deleteQuickReply, @@ -23,32 +49,6 @@ import { import { getEnumLabel, getEnumOptions } from "@/lib/enums" import { Status, StatusLabels } from "@/lib/generated/enums" import { EditDialog } from "./_components/edit" -import { Badge } from "@/components/ui/badge" -import { Button } from "@/components/ui/button" -import { ButtonGroup } from "@/components/ui/button-group" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { Input } from "@/components/ui/input" -import { ListPagination } from "@/components/list-pagination" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table" const listStatusOptions = [ { value: "all", label: "全部状态" }, @@ -60,13 +60,6 @@ const listStatusOptions = [ })), ] as const -function getStatusLabel( - value: string, - options: ReadonlyArray<{ value: string; label: string }> -) { - return options.find((item) => item.value === value)?.label ?? "请选择状态" -} - export default function DashboardQuickRepliesPage() { const [keywordInput, setKeywordInput] = useState("") const [groupNameInput, setGroupNameInput] = useState("") @@ -108,10 +101,6 @@ export default function DashboardQuickRepliesPage() { void loadData() }, [loadData]) - function handleStatusFilterChange(value: string | null) { - setStatusFilterInput(value ?? "all") - } - function applyFilters() { setKeyword(keywordInput) setGroupName(groupNameInput) @@ -221,9 +210,22 @@ export default function DashboardQuickRepliesPage() { return ( <> -
-
-
+ + + + + + } + > +
setGroupNameInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按分组筛选" - className="w-full xl:w-48" + className="w-full sm:w-44" /> - +
+ +
- -
-
-
+ + { + setLimit(nextLimit) + setPage(1) + }} + /> + } + > @@ -282,7 +286,7 @@ export default function DashboardQuickRepliesPage() {
-
+
@@ -345,29 +349,18 @@ export default function DashboardQuickRepliesPage() { ))} - {!loading && result.results.length === 0 ? ( - - - 没有匹配的快捷回复 - - + {loading || result.results.length === 0 ? ( + ) : null}
-
- { - setLimit(nextLimit) - setPage(1) - }} - /> -
-
+ + -
- - -
-
-
+ + + + + + } + > +
+ 拖拽行可调整角色顺序 +
+
+ + + 第 {result.page.page} 页 / 每页 {result.page.limit} 条 + + 共 {result.page.total} 条记录 + + } + > ))} - {!loading && result.results.length === 0 ? ( - - - 暂无角色数据 - - + {loading || result.results.length === 0 ? ( + ) : null} -
-
- - 第 {result.page.page} 页 / 每页 {result.page.limit} 条 - - 共 {result.page.total} 条记录 -
-
+ -
+ ) } diff --git a/web/app/dashboard/tags/page.tsx b/web/app/dashboard/tags/page.tsx index 71820e2..ac820bc 100644 --- a/web/app/dashboard/tags/page.tsx +++ b/web/app/dashboard/tags/page.tsx @@ -44,6 +44,12 @@ import { type Tag, type TagTree, } from "@/lib/api/admin" +import { + DashboardPage, + DashboardTableShell, + DashboardTableStateRow, + DashboardToolbar, +} from "@/components/dashboard-page" import { EditDialog } from "./_components/edit" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" @@ -531,9 +537,28 @@ export default function DashboardTagsPage() { return ( <> -
-
-
+ + + + + + + + } + > +
查询 - - - - -
-
-
+ + ))} - {!loading && flatList.length === 0 ? ( - - - 没有匹配的标签 - - + {loading || flatList.length === 0 ? ( + ) : null} -
-
-
+ + + {children} +
+ ) +} + +export function DashboardPageHeader({ + title, + description, + actions, +}: { + title: string + description?: ReactNode + actions?: ReactNode +}) { + return ( +
+
+

{title}

+ {description ? ( +
+ {description} +
+ ) : null} +
+ {actions ? ( +
{actions}
+ ) : null} +
+ ) +} + +export function DashboardToolbar({ + className, + actions, + children, +}: { + className?: string + actions?: ReactNode + children: ReactNode +}) { + return ( +
+
+ {children} +
+ {actions ? ( +
{actions}
+ ) : null} +
+ ) +} + +export function DashboardTableShell({ + className, + pagination, + children, +}: { + className?: string + pagination?: ReactNode + children: ReactNode +}) { + return ( +
+
+ {children} +
+ {pagination ?
{pagination}
: null} +
+ ) +} + +export function DashboardTableStateRow({ + colSpan, + loading, + loadingText = "正在加载数据...", + emptyText = "暂无数据", +}: { + colSpan: number + loading?: boolean + loadingText?: string + emptyText?: string +}) { + return ( + + + {loading ? loadingText : emptyText} + + + ) +} + +export function DashboardTableSummary({ + children, +}: { + children: ReactNode +}) { + return ( +
+ {children} +
+ ) +}