From 4e95399d4fbb749056a723c850d2a53d74b81561 Mon Sep 17 00:00:00 2001 From: xiaozhu <2395895331@qq.com> Date: Tue, 4 Aug 2026 18:46:18 +0800 Subject: [PATCH] =?UTF-8?q?chore:platform=E6=96=87=E4=BB=B6=E6=8B=86?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app/components/{ => common}/Icon.tsx | 0 frontend/app/components/common/Sidebar.tsx | 73 + frontend/app/components/common/Toast.tsx | 23 + frontend/app/components/common/Topbar.tsx | 121 ++ .../app/components/common/WelcomePanel.tsx | 24 + .../components/platform/CreateFolderModal.tsx | 80 + .../components/platform/CreateScriptModal.tsx | 165 ++ .../app/components/platform/PublishModal.tsx | 101 ++ .../components/platform/ScriptExplorer.tsx | 199 +++ .../components/platform/TreeContextMenu.tsx | 125 ++ .../platform/VersionReceiptModal.tsx | 54 + frontend/app/features/admin/AdminPages.tsx | 2 +- .../platform/ModelPlatformApp copy.tsx | 1513 +++++++++++++++++ .../features/platform/ModelPlatformApp.tsx | 1008 +++-------- .../app/features/platform/ScriptWorkspace.tsx | 2 +- .../app/features/platform/WorkspaceTree.tsx | 2 +- .../app/features/schedules/SchedulePage.tsx | 2 +- 17 files changed, 2701 insertions(+), 793 deletions(-) rename frontend/app/components/{ => common}/Icon.tsx (100%) create mode 100644 frontend/app/components/common/Sidebar.tsx create mode 100644 frontend/app/components/common/Toast.tsx create mode 100644 frontend/app/components/common/Topbar.tsx create mode 100644 frontend/app/components/common/WelcomePanel.tsx create mode 100644 frontend/app/components/platform/CreateFolderModal.tsx create mode 100644 frontend/app/components/platform/CreateScriptModal.tsx create mode 100644 frontend/app/components/platform/PublishModal.tsx create mode 100644 frontend/app/components/platform/ScriptExplorer.tsx create mode 100644 frontend/app/components/platform/TreeContextMenu.tsx create mode 100644 frontend/app/components/platform/VersionReceiptModal.tsx create mode 100644 frontend/app/features/platform/ModelPlatformApp copy.tsx diff --git a/frontend/app/components/Icon.tsx b/frontend/app/components/common/Icon.tsx similarity index 100% rename from frontend/app/components/Icon.tsx rename to frontend/app/components/common/Icon.tsx diff --git a/frontend/app/components/common/Sidebar.tsx b/frontend/app/components/common/Sidebar.tsx new file mode 100644 index 0000000..7a4ffc4 --- /dev/null +++ b/frontend/app/components/common/Sidebar.tsx @@ -0,0 +1,73 @@ +import Icon from "./Icon"; + +const navigation = [ + { label: "工作台", icon: "home" as const, page: "home" as const }, + { label: "构建脚本", icon: "script" as const, page: "scripts" as const }, + { label: "调度配置", icon: "schedule" as const, page: "schedules" as const }, + { label: "系统管理", icon: "settings" as const, page: "system" as const }, +]; + +type ActivePage = "home" | "scripts" | "schedules" | "system"; + +type SidebarProps = { + activePage: ActivePage; + collapsed: boolean; + onNavigate: (page: ActivePage) => void; + onToggleCollapse: () => void; + onEndEditing?: () => void; + onSelectScript?: (scriptId: null) => void; + editSessionRef?: React.RefObject; +}; + +export function Sidebar({ + activePage, + collapsed, + onNavigate, + onToggleCollapse, + onEndEditing, + onSelectScript, + editSessionRef, +}: SidebarProps) { + const handleNavigationClick = (page: ActivePage) => { + if (page !== "scripts") { + if (editSessionRef?.current) { + onEndEditing?.(); + } else { + onSelectScript?.(null); + } + } + onNavigate(page); + }; + + return ( + + ); +} diff --git a/frontend/app/components/common/Toast.tsx b/frontend/app/components/common/Toast.tsx new file mode 100644 index 0000000..fa9e26b --- /dev/null +++ b/frontend/app/components/common/Toast.tsx @@ -0,0 +1,23 @@ +import Icon from "./Icon"; + +type ToastState = { + tone: "success" | "error" | "info"; + message: string; +}; + +type ToastProps = { + toast: ToastState | null; +}; + +export function Toast({ toast }: ToastProps) { + if (!toast) return null; + + return ( +
+ + + + {toast.message} +
+ ); +} diff --git a/frontend/app/components/common/Topbar.tsx b/frontend/app/components/common/Topbar.tsx new file mode 100644 index 0000000..538258e --- /dev/null +++ b/frontend/app/components/common/Topbar.tsx @@ -0,0 +1,121 @@ +import Icon from "./Icon"; +import type { AuthUser, AuthWorkspace } from "../../context/AuthContext"; + +type TopbarProps = { + activePage: string; + apiOnline: boolean; + user: AuthUser | null; + currentWorkspace: AuthWorkspace; + workspaces: AuthWorkspace[]; + workspaceMenuOpen: boolean; + onSetWorkspaceMenuOpen: (open: boolean) => void; + onSetCurrentWorkspace: (workspaceId: string) => void; + onLogout: () => void; +}; + +export function Topbar({ + activePage, + apiOnline, + user, + currentWorkspace, + workspaces, + workspaceMenuOpen, + onSetWorkspaceMenuOpen, + onSetCurrentWorkspace, + onLogout, +}: TopbarProps) { + const pageTitles: Record = { + home: "工作台", + scripts: "构建脚本", + schedules: "调度配置", + system: "系统管理", + }; + + return ( +
+
+ +
+ 开发工作区 +

{pageTitles[activePage] || "工作台"}

+
+
+
+
+ + {apiOnline ? "服务已连接" : "服务未连接"} +
+
+ + {workspaceMenuOpen && ( +
+ {workspaces.map((workspace) => ( + + ))} +
+ )} +
+
+ + +
+
+
+ ); +} diff --git a/frontend/app/components/common/WelcomePanel.tsx b/frontend/app/components/common/WelcomePanel.tsx new file mode 100644 index 0000000..a7e0336 --- /dev/null +++ b/frontend/app/components/common/WelcomePanel.tsx @@ -0,0 +1,24 @@ +import Icon from "./Icon"; + +type WelcomePanelProps = { + onCreateScript: () => void; +}; + +export function WelcomePanel({ onCreateScript }: WelcomePanelProps) { + return ( +
+
+ +
+ 构建脚本工作台 +

创建你的第一个模型脚本

+

+ 通过 Notebook 完成数据探索,或使用 Python 脚本构建可调度的处理任务。 +

+ +
+ ); +} diff --git a/frontend/app/components/platform/CreateFolderModal.tsx b/frontend/app/components/platform/CreateFolderModal.tsx new file mode 100644 index 0000000..7a9ac44 --- /dev/null +++ b/frontend/app/components/platform/CreateFolderModal.tsx @@ -0,0 +1,80 @@ +import { type FormEvent } from "react"; +import Icon from "../common/Icon"; + +type CreateFolderModalProps = { + open: boolean; + parentPath: string; + name: string; + busy: boolean; + onNameChange: (name: string) => void; + onSubmit: (event: FormEvent) => void; + onClose: () => void; +}; + +export function CreateFolderModal({ + open, + parentPath, + name, + busy, + onNameChange, + onSubmit, + onClose, +}: CreateFolderModalProps) { + if (!open) return null; + + return ( +
+
+
+
+ WORKSPACE +

新建文件夹

+
+ +
+
+
+ + 创建到:{parentPath || "个人根目录"} +
+ +
+ + +
+
+
+
+ ); +} diff --git a/frontend/app/components/platform/CreateScriptModal.tsx b/frontend/app/components/platform/CreateScriptModal.tsx new file mode 100644 index 0000000..9c8a919 --- /dev/null +++ b/frontend/app/components/platform/CreateScriptModal.tsx @@ -0,0 +1,165 @@ +import { type FormEvent } from "react"; +import Icon from "../common/Icon"; +import type { ScriptType, Visibility } from "../../services/api"; + +type NewScriptForm = { + name: string; + scriptType: ScriptType; + visibility: Visibility; + parentPath: string; +}; + +type CreateScriptModalProps = { + open: boolean; + creating: boolean; + form: NewScriptForm; + scripts: Array<{ script_type: ScriptType; script_name: string }>; + onFormChange: (form: NewScriptForm) => void; + onSubmit: (event: FormEvent) => void; + onClose: () => void; +}; + +export function CreateScriptModal({ + open, + creating, + form, + scripts, + onFormChange, + onSubmit, + onClose, +}: CreateScriptModalProps) { + if (!open) return null; + + const suffix = form.scriptType === "notebook" ? ".ipynb" : ".py"; + const requestedName = form.name.trim(); + const normalizedName = requestedName.toLocaleLowerCase().endsWith(suffix) + ? requestedName + : `${requestedName}${suffix}`; + const duplicate = scripts.some((script) => + script.script_type === form.scriptType + && script.script_name.toLocaleLowerCase() === normalizedName.toLocaleLowerCase() + ); + + return ( +
+
+
+
+ 工作副本 +

新建构建脚本

+
+ +
+
+
+ + 保存到:{form.parentPath || "个人根目录"} +
+ + +
+ 脚本类型 + + +
+ + + +
+ + +
+
+
+
+ ); +} diff --git a/frontend/app/components/platform/PublishModal.tsx b/frontend/app/components/platform/PublishModal.tsx new file mode 100644 index 0000000..867a9fe --- /dev/null +++ b/frontend/app/components/platform/PublishModal.tsx @@ -0,0 +1,101 @@ +import { type FormEvent } from "react"; +import Icon from "../common/Icon"; +import type { ScriptItem, Visibility } from "../../services/api"; +import { scriptIcon } from "../../features/platform/WorkspaceTree"; + +type PublishModalProps = { + publishTarget: ScriptItem | null; + releaseNote: string; + publishVisibility: Visibility; + publishing: boolean; + onReleaseNoteChange: (note: string) => void; + onPublishVisibilityChange: (visibility: Visibility) => void; + onSubmit: (event: FormEvent) => void; + onClose: () => void; +}; + +export function PublishModal({ + publishTarget, + releaseNote, + publishVisibility, + publishing, + onReleaseNoteChange, + onPublishVisibilityChange, + onSubmit, + onClose, +}: PublishModalProps) { + if (!publishTarget) return null; + + return ( +
+
+
+
+ 不可变制品 +

发布稳定版本

+
+ +
+
+
+ + + + + {publishTarget.script_name} + 当前 Workspace 工作副本 + +
+