From a9c70effaea36d707f3486e6712030b7c557a19b Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Wed, 5 Aug 2026 15:05:16 +0800 Subject: [PATCH] refactor(frontend): unify list and create pages with compact topbar layout Move resource list and guide page titles into the shared topbar, introduce ListPageLayout for tighter spacing, and align knowledge edit with the prompt editor chrome. Co-authored-by: Cursor --- frontend/src/app/globals.css | 20 + .../assistant-editor/editor-controls.tsx | 10 +- .../components/layout/list-page-layout.tsx | 80 +++ .../src/components/pages/AssistantPage.tsx | 164 +++-- .../pages/ComponentsKnowledgePage.tsx | 650 ++++++++---------- .../components/pages/ComponentsModelsPage.tsx | 25 +- .../components/pages/ComponentsToolsPage.tsx | 26 +- frontend/src/components/pages/HistoryPage.tsx | 19 +- frontend/src/components/ui/list-toolbar.tsx | 2 +- 9 files changed, 529 insertions(+), 467 deletions(-) create mode 100644 frontend/src/components/layout/list-page-layout.tsx diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index a95698d..1be79c7 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -241,6 +241,26 @@ padding: 0; } + /* Resource list pages use a tighter content band below the shared topbar. */ + .app-content:has([data-app-content="list"]) { + padding-top: 1rem; + padding-bottom: 1.5rem; + } + + @media (min-width: 640px) { + .app-content:has([data-app-content="list"]) { + padding-top: 1.25rem; + padding-bottom: 2rem; + } + } + + @media (min-width: 1024px) { + .app-content:has([data-app-content="list"]) { + padding-top: 1.5rem; + padding-bottom: 2.5rem; + } + } + .scrollbar-subtle { scrollbar-width: thin; scrollbar-color: color-mix( diff --git a/frontend/src/components/assistant-editor/editor-controls.tsx b/frontend/src/components/assistant-editor/editor-controls.tsx index 5d645d6..563e2ce 100644 --- a/frontend/src/components/assistant-editor/editor-controls.tsx +++ b/frontend/src/components/assistant-editor/editor-controls.tsx @@ -50,14 +50,20 @@ import type { import type { RuntimeMode } from "./types"; -export function EditorBackButton({ onClick }: { onClick: () => void }) { +export function EditorBackButton({ + onClick, + ariaLabel = "返回助手列表", +}: { + onClick: () => void; + ariaLabel?: string; +}) { return ( diff --git a/frontend/src/components/layout/list-page-layout.tsx b/frontend/src/components/layout/list-page-layout.tsx new file mode 100644 index 0000000..8d2afbb --- /dev/null +++ b/frontend/src/components/layout/list-page-layout.tsx @@ -0,0 +1,80 @@ +"use client"; + +import type { ReactNode } from "react"; + +import { cn } from "@/lib/utils"; + +import { PageTopbar, TopbarPortal } from "./topbar-portal"; + +export function ListPageLayout({ + title, + description, + action, + children, + className, +}: { + title: string; + description?: ReactNode; + action?: ReactNode; + children: ReactNode; + className?: string; +}) { + const hasHeader = Boolean(description || action); + + return ( + <> + + + + +
+ {hasHeader && ( +
+ {description && ( +

+ {description} +

+ )} + {action} +
+ )} + + {children} +
+ + ); +} + +export function ListPageSection({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/frontend/src/components/pages/AssistantPage.tsx b/frontend/src/components/pages/AssistantPage.tsx index 94e2de8..33ad705 100644 --- a/frontend/src/components/pages/AssistantPage.tsx +++ b/frontend/src/components/pages/AssistantPage.tsx @@ -33,10 +33,9 @@ import { } from "@/components/ui/dropdown-menu"; import { DataList } from "@/components/ui/data-list"; import { - PageTopbar, - TopbarPortal, -} from "@/components/layout/topbar-portal"; -import { PageHeader } from "@/components/ui/page-header"; + ListPageLayout, + ListPageSection, +} from "@/components/layout/list-page-layout"; import { FilterPills } from "@/components/ui/filter-pills"; import { SearchInput } from "@/components/ui/search-input"; import { ListToolbar } from "@/components/ui/list-toolbar"; @@ -538,9 +537,10 @@ export function AssistantPage(props: AssistantPageProps) { } // 删除助手 - async function handleDelete(id: string) { + async function handleDelete(assistant: AssistantListItem) { + if (!window.confirm(`确认删除助手“${assistant.name}”?`)) return; try { - await assistantsApi.remove(id); + await assistantsApi.remove(assistant.id); await loadAssistants(); } catch (error) { setListError(error instanceof Error ? error.message : "删除失败"); @@ -997,23 +997,17 @@ export function AssistantPage(props: AssistantPageProps) { if (view === "list") { return ( -
- - - 创建助手 - - } - /> - -
+ + + 创建助手 + + } + > + void loadAssistants()} + onRowClick={handleEdit} empty={{ title: listItems.length === 0 ? "暂无助手" : "未找到匹配的助手", description: @@ -1116,12 +1111,19 @@ export function AssistantPage(props: AssistantPageProps) { align: "right", cellClassName: "flex justify-end gap-2", cell: (assistant) => ( - <> +
event.stopPropagation()} + onPointerDown={(event) => event.stopPropagation()} + > @@ -1154,7 +1157,10 @@ export function AssistantPage(props: AssistantPageProps) { className="rounded-lg" onSelect={(event) => { event.preventDefault(); - void handleDelete(assistant.id); + window.setTimeout( + () => void handleDelete(assistant), + 0, + ); }} > @@ -1162,59 +1168,52 @@ export function AssistantPage(props: AssistantPageProps) { - +
), }, ]} /> -
-
+ + ); } if (view === "choose") { return ( - <> - - router.push("/assistants")} - > - - 返回列表 - - } - /> - + router.push("/assistants")} + > + + 返回列表 + + } + > + + + -
-

- 先为助手取个名字,再选择构建方式。确认后将立即创建助手并进入编辑页。 -

+
+
构建方式
-
- -
- -
-
构建方式
- -
+
{assistantTypeOptions.map((option) => { const selected = draftType === option.type; @@ -1264,21 +1263,19 @@ export function AssistantPage(props: AssistantPageProps) {
-
- {createError && ( - {createError} - )} - +
+ {createError && ( + {createError} + )} + -
- + ); } diff --git a/frontend/src/components/pages/ComponentsKnowledgePage.tsx b/frontend/src/components/pages/ComponentsKnowledgePage.tsx index 07f17d8..efd0078 100644 --- a/frontend/src/components/pages/ComponentsKnowledgePage.tsx +++ b/frontend/src/components/pages/ComponentsKnowledgePage.tsx @@ -22,7 +22,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { DataList } from "@/components/ui/data-list"; import { Dialog, @@ -40,7 +39,17 @@ import { } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { ListToolbar } from "@/components/ui/list-toolbar"; -import { PageHeader } from "@/components/ui/page-header"; +import { + AssistantIdentity, + EditorBackButton, + EditableTitle, +} from "@/components/assistant-editor/editor-controls"; +import { SectionCard } from "@/components/editor/section-card"; +import { + ListPageLayout, + ListPageSection, +} from "@/components/layout/list-page-layout"; +import { TopbarPortal } from "@/components/layout/topbar-portal"; import { SearchInput } from "@/components/ui/search-input"; import { Select, @@ -196,23 +205,20 @@ function KnowledgeListView() { } return ( -
- router.push("/components/knowledge/new")} - > - - 添加知识库 - - } - /> - -
+ router.push("/components/knowledge/new")} + > + + 添加知识库 + + } + > + -
-
+ + ); } @@ -425,26 +431,21 @@ function KnowledgeCreateView() { } return ( -
-
-
-

添加知识库

-

- 填写名称、说明并选择 Embedding 模型。确认后将立即创建并进入编辑页。 -

-
- + router.push("/components/knowledge")} > 返回列表 -
- + } + > {loading ? (
@@ -452,57 +453,65 @@ function KnowledgeCreateView() {
) : ( <> -
-
- - -