feat(ui): new WorkflowElements using antd

This commit is contained in:
Fu Diwei
2025-01-03 20:29:34 +08:00
parent c6a8f923e4
commit 8a16893082
43 changed files with 805 additions and 810 deletions

View File

@@ -1,6 +1,6 @@
import { Navigate, Outlet } from "react-router-dom";
import Version from "@/components/core/Version";
import Version from "@/components/Version";
import { getPocketBase } from "@/repository/pocketbase";
const AuthLayout = () => {

View File

@@ -15,7 +15,7 @@ import {
} from "@ant-design/icons";
import { Button, type ButtonProps, Drawer, Dropdown, Layout, Menu, type MenuProps, Tooltip, theme } from "antd";
import Version from "@/components/core/Version";
import Version from "@/components/Version";
import { useBrowserTheme } from "@/hooks";
import { getPocketBase } from "@/repository/pocketbase";
@@ -66,7 +66,7 @@ const ConsoleLayout = () => {
<Layout className="pl-[256px] max-md:pl-0">
<Layout.Header className="sticky top-0 left-0 right-0 p-0 z-[19] shadow-sm" style={{ background: themeToken.colorBgContainer }}>
<div className="flex items-center justify-between size-full px-4 overflow-hidden">
<div className="flex items-center gap-4 size-full">
<div className="flex items-center gap-4">
<Button className="md:hidden" icon={<MenuOutlinedIcon />} size="large" onClick={handleSiderOpen} />
<Drawer
closable={false}

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
import {
@@ -19,13 +19,11 @@ import { isEqual } from "radash";
import { z } from "zod";
import { run as runWorkflow } from "@/api/workflow";
import ModalForm from "@/components/ModalForm";
import Show from "@/components/Show";
import ModalForm from "@/components/core/ModalForm";
import End from "@/components/workflow/End";
import NodeRender from "@/components/workflow/NodeRender";
import WorkflowProvider from "@/components/workflow/WorkflowProvider";
import WorkflowRuns from "@/components/workflow/run/WorkflowRuns";
import { type WorkflowModel, type WorkflowNode, isAllNodesValidated } from "@/domain/workflow";
import WorkflowElements from "@/components/workflow/WorkflowElements";
import WorkflowRuns from "@/components/workflow/WorkflowRuns";
import { type WorkflowModel, isAllNodesValidated } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { remove as removeWorkflow } from "@/repository/workflow";
import { useWorkflowStore } from "@/stores/workflow";
@@ -41,33 +39,21 @@ const WorkflowDetail = () => {
const [notificationApi, NotificationContextHolder] = notification.useNotification();
const { id: workflowId } = useParams();
const { workflow, init, save, setBaseInfo, switchEnable } = useWorkflowStore(
useZustandShallowSelector(["workflow", "init", "save", "setBaseInfo", "switchEnable"])
const { workflow, initialized, init, save, destroy, setBaseInfo, switchEnable } = useWorkflowStore(
useZustandShallowSelector(["workflow", "initialized", "init", "destroy", "save", "setBaseInfo", "switchEnable"])
);
useEffect(() => {
// TODO: loading
init(workflowId!);
return () => {
destroy();
};
}, [workflowId]);
const [tabValue, setTabValue] = useState<"orchestration" | "runs">("orchestration");
const workflowNodes = useMemo(() => {
let current = workflow.draft as WorkflowNode;
const elements: JSX.Element[] = [];
while (current) {
// 处理普通节点
elements.push(<NodeRender data={current} key={current.id} />);
current = current.next as WorkflowNode;
}
elements.push(<End key="workflow-end" />);
return elements;
}, [workflow]);
const [workflowRunning, setWorkflowRunning] = useState(false);
const [isRunning, setIsRunning] = useState(false);
const [allowDiscard, setAllowDiscard] = useState(false);
const [allowRelease, setAllowRelease] = useState(false);
@@ -75,10 +61,10 @@ const WorkflowDetail = () => {
useDeepCompareEffect(() => {
const hasReleased = !!workflow.content;
const hasChanges = workflow.hasDraft! || !isEqual(workflow.draft, workflow.content);
setAllowDiscard(!workflowRunning && hasReleased && hasChanges);
setAllowRelease(!workflowRunning && hasChanges);
setAllowDiscard(!isRunning && hasReleased && hasChanges);
setAllowRelease(!isRunning && hasChanges);
setAllowRun(hasReleased);
}, [workflow, workflowRunning]);
}, [workflow, isRunning]);
const handleBaseInfoFormFinish = async (values: Pick<WorkflowModel, "name" | "description">) => {
try {
@@ -174,7 +160,7 @@ const WorkflowDetail = () => {
// TODO: 异步执行
promise.then(async () => {
setWorkflowRunning(true);
setIsRunning(true);
try {
await runWorkflow(workflowId!);
@@ -188,7 +174,7 @@ const WorkflowDetail = () => {
console.error(err);
messageApi.warning(t("common.text.operation_failed"));
} finally {
setWorkflowRunning(false);
setIsRunning(false);
}
});
};
@@ -203,35 +189,44 @@ const WorkflowDetail = () => {
<PageHeader
style={{ paddingBottom: 0 }}
title={workflow.name}
extra={[
<WorkflowBaseInfoModalForm key="edit" data={workflow} trigger={<Button>{t("common.button.edit")}</Button>} onFinish={handleBaseInfoFormFinish} />,
extra={
initialized
? [
<WorkflowBaseInfoModalForm
key="edit"
data={workflow}
trigger={<Button>{t("common.button.edit")}</Button>}
onFinish={handleBaseInfoFormFinish}
/>,
<Button key="enable" onClick={handleEnableChange}>
{workflow.enabled ? t("workflow.action.disable") : t("workflow.action.enable")}
</Button>,
<Button key="enable" onClick={handleEnableChange}>
{workflow.enabled ? t("workflow.action.disable") : t("workflow.action.enable")}
</Button>,
<Dropdown
key="more"
menu={{
items: [
{
key: "delete",
label: t("workflow.action.delete"),
danger: true,
icon: <DeleteOutlinedIcon />,
onClick: () => {
handleDeleteClick();
},
},
],
}}
trigger={["click"]}
>
<Button icon={<DownOutlinedIcon />} iconPosition="end">
{t("common.button.more")}
</Button>
</Dropdown>,
]}
<Dropdown
key="more"
menu={{
items: [
{
key: "delete",
label: t("workflow.action.delete"),
danger: true,
icon: <DeleteOutlinedIcon />,
onClick: () => {
handleDeleteClick();
},
},
],
}}
trigger={["click"]}
>
<Button icon={<DownOutlinedIcon />} iconPosition="end">
{t("common.button.more")}
</Button>
</Dropdown>,
]
: []
}
>
<Typography.Paragraph type="secondary">{workflow.description}</Typography.Paragraph>
<Tabs
@@ -249,15 +244,15 @@ const WorkflowDetail = () => {
</Card>
<div className="p-4">
<Card>
<Card loading={!initialized}>
<Show when={tabValue === "orchestration"}>
<div className="relative">
<div className="flex flex-col items-center py-12 pr-48">
<WorkflowProvider>{workflowNodes}</WorkflowProvider>
<div className="py-12 lg:pr-36 xl:pr-48">
<WorkflowElements />
</div>
<div className="absolute top-0 right-0 z-[1]">
<Space>
<Button disabled={!allowRun} icon={<CaretRightOutlinedIcon />} loading={workflowRunning} type="primary" onClick={handleRunClick}>
<Button disabled={!allowRun} icon={<CaretRightOutlinedIcon />} loading={isRunning} type="primary" onClick={handleRunClick}>
{t("workflow.detail.orchestration.action.run")}
</Button>