mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(ui): workflow designer node drawer base
This commit is contained in:
Generated
+317
-317
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -14,9 +14,9 @@
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.11.2",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@flowgram.ai/document": "^0.3.3",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.3.3",
|
||||
"@flowgram.ai/minimap-plugin": "^0.3.3",
|
||||
"@flowgram.ai/document": "^0.3.4",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.3.4",
|
||||
"@flowgram.ai/minimap-plugin": "^0.3.4",
|
||||
"@tabler/icons-react": "^3.34.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "^4.24.2",
|
||||
"@uiw/codemirror-theme-vscode": "^4.24.2",
|
||||
@@ -37,9 +37,9 @@
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-router-dom": "^7.7.1",
|
||||
"react-router-dom": "^7.8.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"zod": "^4.0.14",
|
||||
"zod": "^4.0.15",
|
||||
"zustand": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -59,14 +59,14 @@
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"fs-extra": "^11.3.0",
|
||||
"fs-extra": "^11.3.1",
|
||||
"prettier": "^3.6.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.38.0",
|
||||
"typescript-eslint": "^8.39.0",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { Button, Drawer, type DrawerProps, Flex, Form, type FormProps, type ModalProps } from "antd";
|
||||
|
||||
@@ -73,6 +74,7 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
|
||||
|
||||
const mergedDrawerProps: DrawerProps = {
|
||||
...drawerProps,
|
||||
closeIcon: false,
|
||||
afterOpenChange: (open) => {
|
||||
if (!open && !mergedFormProps.preserve) {
|
||||
formInst.resetFields();
|
||||
@@ -109,7 +111,7 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
|
||||
<Drawer
|
||||
{...mergedDrawerProps}
|
||||
footer={
|
||||
<Flex justify="end" gap="small">
|
||||
<Flex className="px-2" justify="end" gap="small">
|
||||
<Button {...cancelButtonProps} onClick={handleCancelClick}>
|
||||
{cancelText ?? t("common.button.cancel")}
|
||||
</Button>
|
||||
@@ -120,7 +122,21 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
|
||||
}
|
||||
forceRender
|
||||
open={open}
|
||||
title={title}
|
||||
title={
|
||||
<Flex align="center" justify="space-between" gap="small">
|
||||
<div className="flex-1 truncate">{title}</div>
|
||||
{mergedDrawerProps.closeIcon !== false && (
|
||||
<Button
|
||||
className="ant-drawer-close"
|
||||
style={{ marginInline: 0 }}
|
||||
icon={mergedDrawerProps.closeIcon ?? <IconX size="1.25em" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={handleCancelClick}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
}
|
||||
width={width}
|
||||
>
|
||||
<Form className={className} style={style} {...mergedFormProps} form={formInst}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { App, Button, Drawer, Flex } from "antd";
|
||||
|
||||
@@ -99,11 +100,11 @@ const AccessEditDrawer = ({ mode, data, loading, trigger, usage, afterSubmit, ..
|
||||
|
||||
<Drawer
|
||||
afterOpenChange={setOpen}
|
||||
closable={!formPending}
|
||||
closeIcon={false}
|
||||
destroyOnHidden
|
||||
footer={
|
||||
footerShow ? (
|
||||
<Flex justify="end" gap="small">
|
||||
<Flex className="px-2" justify="end" gap="small">
|
||||
<Button onClick={handleCancelClick}>{t("common.button.cancel")}</Button>
|
||||
<Button loading={formPending} type="primary" onClick={handleOkClick}>
|
||||
{mode === "edit" ? t("common.button.save") : t("common.button.submit")}
|
||||
@@ -116,9 +117,23 @@ const AccessEditDrawer = ({ mode, data, loading, trigger, usage, afterSubmit, ..
|
||||
loading={loading}
|
||||
maskClosable={!formPending}
|
||||
open={open}
|
||||
title={t(`access.action.${mode}.modal.title`)}
|
||||
width={720}
|
||||
onClose={() => setOpen(false)}
|
||||
size="large"
|
||||
title={
|
||||
<Flex align="center" justify="space-between" gap="small">
|
||||
<div className="flex-1 truncate">
|
||||
{mode === "edit" && !!data ? t("access.action.edit.modal.title") + ` #${data.id}` : t(`access.action.${mode}.modal.title`)}
|
||||
</div>
|
||||
<Button
|
||||
className="ant-drawer-close"
|
||||
style={{ marginInline: 0 }}
|
||||
icon={<IconX size="1.25em" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={handleCancelClick}
|
||||
/>
|
||||
</Flex>
|
||||
}
|
||||
onClose={handleCancelClick}
|
||||
>
|
||||
<AccessForm ref={formRef} disabled={formPending} initialValues={data} mode={mode} usage={usage} onValuesChange={handleFormValuesChange} />
|
||||
</Drawer>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { Drawer } from "antd";
|
||||
import { Button, Drawer, Flex } from "antd";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { type CertificateModel } from "@/domain/certificate";
|
||||
@@ -30,12 +31,25 @@ const CertificateDetailDrawer = ({ data, loading, trigger, ...props }: Certifica
|
||||
|
||||
<Drawer
|
||||
afterOpenChange={setOpen}
|
||||
closeIcon={false}
|
||||
destroyOnHidden
|
||||
open={open}
|
||||
loading={loading}
|
||||
placement="right"
|
||||
title={`Certificate #${data?.id}`}
|
||||
width={720}
|
||||
size="large"
|
||||
title={
|
||||
<Flex align="center" justify="space-between" gap="small">
|
||||
<div className="flex-1 truncate">{data ? `Certificate #${data.id}` : "Certificate"}</div>
|
||||
<Button
|
||||
className="ant-drawer-close"
|
||||
style={{ marginInline: 0 }}
|
||||
icon={<IconX size="1.25em" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => setOpen(false)}
|
||||
/>
|
||||
</Flex>
|
||||
}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<Show when={!!data}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { Drawer } from "antd";
|
||||
import { Button, Drawer, Flex } from "antd";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { type WorkflowRunModel } from "@/domain/workflowRun";
|
||||
@@ -31,12 +32,25 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR
|
||||
|
||||
<Drawer
|
||||
afterOpenChange={setOpen}
|
||||
closeIcon={false}
|
||||
destroyOnHidden
|
||||
open={open}
|
||||
loading={loading}
|
||||
placement="right"
|
||||
title={`WorkflowRun #${data?.id}`}
|
||||
width={720}
|
||||
size="large"
|
||||
title={
|
||||
<Flex align="center" justify="space-between" gap="small">
|
||||
<div className="flex-1 truncate">{data ? `Workflow Run #${data.id}` : "Workflow Run"}</div>
|
||||
<Button
|
||||
className="ant-drawer-close"
|
||||
style={{ marginInline: 0 }}
|
||||
icon={<IconX size="1.25em" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => setOpen(false)}
|
||||
/>
|
||||
</Flex>
|
||||
}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<Show when={!!data}>
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { Anchor, type AnchorProps, App, Button, Drawer, Flex, Typography } from "antd";
|
||||
|
||||
import { useTriggerElement } from "@/hooks";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
|
||||
import { type NodeRegistry } from "./nodes/typings";
|
||||
|
||||
export interface NodeDrawerProps {
|
||||
children?: React.ReactNode;
|
||||
anchor?: Pick<AnchorProps, "items"> | false;
|
||||
confirmLoading?: boolean;
|
||||
loading?: boolean;
|
||||
footer?: boolean;
|
||||
node?: FlowNodeEntity;
|
||||
open?: boolean;
|
||||
trigger?: React.ReactNode;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const NodeDrawer = (_: NodeDrawerProps) => {
|
||||
const { children, anchor, confirmLoading, footer = true, loading, node, trigger, ...props } = _;
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { notification } = App.useApp();
|
||||
|
||||
const [open, setOpen] = useControllableValue<boolean>(props, {
|
||||
valuePropName: "open",
|
||||
defaultValuePropName: "defaultOpen",
|
||||
trigger: "onOpenChange",
|
||||
});
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) });
|
||||
|
||||
// const nodeRender = useNodeRender(node);
|
||||
const nodeRegistry = node?.getNodeRegistry<NodeRegistry>();
|
||||
const NodeIcon = nodeRegistry?.meta?.icon;
|
||||
const renderNodeIcon = () =>
|
||||
NodeIcon == null ? null : (
|
||||
<div
|
||||
className="flex size-6 items-center justify-center rounded-lg bg-white text-primary shadow-md dark:bg-stone-200"
|
||||
style={{
|
||||
color: nodeRegistry?.meta?.iconColor,
|
||||
backgroundColor: nodeRegistry?.meta?.iconBgColor,
|
||||
}}
|
||||
>
|
||||
<NodeIcon size="1em" color={nodeRegistry?.meta?.iconColor} stroke="1.25" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const handleOkClick = async () => {
|
||||
setFormPending(true);
|
||||
try {
|
||||
// TODO:
|
||||
} catch (err) {
|
||||
setFormPending(false);
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO:
|
||||
|
||||
setOpen(false);
|
||||
} catch (err) {
|
||||
notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
setFormPending(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelClick = () => {
|
||||
if (confirmLoading) return;
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{triggerEl}
|
||||
|
||||
<Drawer
|
||||
styles={{
|
||||
header: {
|
||||
paddingBottom: anchor != null && anchor !== false ? 0 : undefined,
|
||||
},
|
||||
}}
|
||||
afterOpenChange={setOpen}
|
||||
closeIcon={false}
|
||||
destroyOnHidden
|
||||
footer={
|
||||
footer ? (
|
||||
<Flex className="px-2" justify="end" gap="small">
|
||||
<Button onClick={handleCancelClick}>{t("common.button.cancel")}</Button>
|
||||
<Button loading={confirmLoading} type="primary" onClick={handleOkClick}>
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Flex>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
loading={loading}
|
||||
maskClosable={!confirmLoading}
|
||||
open={open}
|
||||
size="large"
|
||||
title={
|
||||
<>
|
||||
<Flex align="center" justify="space-between" gap="small">
|
||||
<div>{renderNodeIcon()}</div>
|
||||
<div className="flex-1 truncate">{node?.toJSON()?.data?.name}</div>
|
||||
<Button
|
||||
className="ant-drawer-close"
|
||||
style={{ marginInline: 0 }}
|
||||
icon={<IconX size="1.25em" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={handleCancelClick}
|
||||
/>
|
||||
</Flex>
|
||||
<div className="mt-3 text-sm font-normal">
|
||||
<Typography.Text className="text-xs" type="secondary">
|
||||
<span>{t("workflow.detail.design.drawer.node_id.label")}</span>
|
||||
<span>{node?.id}</span>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
{anchor != null && anchor !== false && (
|
||||
<div className="mt-3 text-sm font-normal">
|
||||
<Anchor affix={false} getContainer={() => containerRef.current!} direction="horizontal" items={anchor.items} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
onClose={handleCancelClick}
|
||||
>
|
||||
<div ref={containerRef}>{children}</div>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const useProps = () => {
|
||||
const [node, setNode] = useState<NodeDrawerProps["node"]>();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
const onOpenChange = (open: boolean) => {
|
||||
setOpen(open);
|
||||
|
||||
if (!open) {
|
||||
setNode(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
node,
|
||||
open,
|
||||
setNode,
|
||||
setOpen,
|
||||
onOpenChange,
|
||||
};
|
||||
};
|
||||
|
||||
const _default = Object.assign(NodeDrawer, {
|
||||
useProps,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -29,6 +29,12 @@ const Node = (_: NodeProps) => {
|
||||
}, [nodeRender.form]);
|
||||
|
||||
const { onNodeClick } = useEditorContext();
|
||||
const handleNodeClick = () => {
|
||||
const node = nodeRender.node;
|
||||
if (node.getNodeRegistry<NodeRegistry>().meta?.clickable) {
|
||||
onNodeClick?.(node);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -42,9 +48,7 @@ const Node = (_: NodeProps) => {
|
||||
nodeRender.startDrag(e);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={() => {
|
||||
onNodeClick?.(nodeRender.node);
|
||||
}}
|
||||
onClick={handleNodeClick}
|
||||
>
|
||||
<NodeRenderContextProvider value={nodeRender}>{nodeRender.form?.render()}</NodeRenderContextProvider>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,8 @@ export const BizApplyNodeRegistry: NodeRegistry = {
|
||||
icon: IconContract,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
@@ -91,6 +93,8 @@ export const BizUploadNodeRegistry: NodeRegistry = {
|
||||
icon: IconCloudUpload,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
@@ -147,6 +151,8 @@ export const BizMonitorNodeRegistry: NodeRegistry = {
|
||||
icon: IconDeviceDesktopSearch,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
@@ -194,6 +200,8 @@ export const BizDeployNodeRegistry: NodeRegistry = {
|
||||
icon: IconPackage,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
@@ -265,6 +273,8 @@ export const BizNotifyNodeRegistry: NodeRegistry = {
|
||||
icon: IconSend,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#0693d4",
|
||||
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
|
||||
@@ -19,7 +19,9 @@ export const ConditionNodeRegistry: NodeRegistry = {
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#373c43",
|
||||
|
||||
clickable: false,
|
||||
expandable: false,
|
||||
|
||||
deleteDisable: false,
|
||||
},
|
||||
|
||||
@@ -68,6 +70,12 @@ export const BranchBlockNodeRegistry: NodeRegistry = {
|
||||
meta: {
|
||||
labelText: getI18n().t("workflow_node.branch_block.label"),
|
||||
|
||||
icon: IconSitemap,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#373c43",
|
||||
|
||||
clickable: true,
|
||||
|
||||
addDisable: true,
|
||||
copyDisable: true,
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ export const EndNodeRegistry: NodeRegistry = {
|
||||
|
||||
isNodeEnd: true,
|
||||
|
||||
clickable: false,
|
||||
expandable: false,
|
||||
selectable: false,
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export const StartNodeRegistry: NodeRegistry = {
|
||||
|
||||
isStart: true,
|
||||
|
||||
clickable: true,
|
||||
expandable: false,
|
||||
selectable: false,
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export const TryCatchNodeRegistry: NodeRegistry = {
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#373c43",
|
||||
|
||||
clickable: false,
|
||||
expandable: false,
|
||||
},
|
||||
|
||||
@@ -69,6 +70,7 @@ export const CatchBlockNodeRegistry: NodeRegistry = {
|
||||
meta: {
|
||||
labelText: getI18n().t("workflow_node.catch_block.label"),
|
||||
|
||||
clickable: false,
|
||||
draggable: false,
|
||||
|
||||
addDisable: true,
|
||||
|
||||
@@ -69,6 +69,7 @@ const InternalNodeCard = ({
|
||||
nodeRender: NodeRenderReturnType;
|
||||
}) => {
|
||||
const nodeRenderData = nodeRender.node.getData(FlowNodeRenderData)!;
|
||||
const nodeRegistry = nodeRender.node.getNodeRegistry<NodeRegistry>();
|
||||
|
||||
const isActivated = useMemo(() => nodeRenderData.activated || nodeRenderData.lineActivated, [nodeRenderData.activated, nodeRenderData.lineActivated]);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
@@ -79,7 +80,12 @@ const InternalNodeCard = ({
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={mergeCls("relative rounded-xl shadow-sm", { "border-primary": isActivated }, className)}
|
||||
className={mergeCls(
|
||||
"relative rounded-xl shadow-sm",
|
||||
{ "border-primary": isActivated },
|
||||
nodeRegistry.meta?.clickable ? "cursor-pointer" : "cursor-default",
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
styles={{ body: { padding: 0 } }}
|
||||
hoverable
|
||||
@@ -299,7 +305,7 @@ export const BaseNode = ({ className, style, children }: BaseNodeProps) => {
|
||||
placement="rightTop"
|
||||
>
|
||||
<div className="group/node relative">
|
||||
<InternalNodeCard className={mergeCls("w-[320px] ", className)} style={style} nodeRender={nodeRender}>
|
||||
<InternalNodeCard className={mergeCls("w-[320px]", className)} style={style} nodeRender={nodeRender}>
|
||||
<div className={mergeCls("flex items-center gap-1 overflow-hidden p-3", inputVisible ? "invisible" : "visible")}>
|
||||
{nodeRegistry.meta?.helpText == null ? (
|
||||
renderNodeIcon()
|
||||
|
||||
@@ -40,13 +40,13 @@ export interface NodeMeta extends FlowNodeMeta {
|
||||
style?: React.CSSProperties;
|
||||
helpText?: React.ReactNode;
|
||||
labelText?: React.ReactNode;
|
||||
icon?: React.Component;
|
||||
icon?: React.ExoticComponent<any> | React.ComponentType<any>;
|
||||
iconColor?: string;
|
||||
iconBgColor?: string;
|
||||
clickable?: boolean;
|
||||
}
|
||||
|
||||
export interface NodeRegistry<V extends NodeJSON["data"] = NodeJSON["data"]> extends FlowNodeRegistry<NodeMeta> {
|
||||
meta?: FlowNodeMeta;
|
||||
formMeta?: Omit<FormMeta<V>, "render"> & {
|
||||
render: (props: FormRenderProps<V>) => React.ReactElement;
|
||||
};
|
||||
|
||||
@@ -58,6 +58,11 @@
|
||||
svg.icon {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Fix antd drawer title overflow */
|
||||
.ant-drawer .ant-drawer-title {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"workflow.detail.design.toolbar.minimap": "Minimap",
|
||||
"workflow.detail.design.toolbar.hand_mode": "Hand mode",
|
||||
"workflow.detail.design.toolbar.pointer_mode": "Pointer mode",
|
||||
"workflow.detail.design.drawer.node_id.label": "Node ID: ",
|
||||
"workflow.detail.design.action.publish.button": "Publish",
|
||||
"workflow.detail.design.action.publish.modal.title": "Publish changes",
|
||||
"workflow.detail.design.action.publish.modal.content": "Are you sure to publish your changes?",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"workflow.detail.design.toolbar.minimap": "小地图",
|
||||
"workflow.detail.design.toolbar.hand_mode": "触摸模式",
|
||||
"workflow.detail.design.toolbar.pointer_mode": "指针模式",
|
||||
"workflow.detail.design.drawer.node_id.label": "节点 ID:",
|
||||
"workflow.detail.design.action.publish.button": "发布更改",
|
||||
"workflow.detail.design.action.publish.modal.title": "发布更改",
|
||||
"workflow.detail.design.action.publish.modal.content": "确定要发布更改吗?",
|
||||
|
||||
@@ -9,7 +9,8 @@ import { debounce, isEqual } from "radash";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import WorkflowEditor, { type EditorInstance as WorkflowEditorInstance } from "@/components/workflow/designer/Editor";
|
||||
import WorkflowEditorToolbar from "@/components/workflow/designer/Toolbar";
|
||||
import WorkflowNodeDrawer from "@/components/workflow/designer/NodeDrawer";
|
||||
import WorkflowToolbar from "@/components/workflow/designer/Toolbar";
|
||||
import { type WorkflowNode, WorkflowNodeType } from "@/domain/workflow";
|
||||
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
@@ -59,6 +60,8 @@ const WorkflowDetailDesign = () => {
|
||||
return () => disposable?.dispose();
|
||||
}, []);
|
||||
|
||||
const { setNode: setDrawerNode, setOpen: setNodeDrawerOpen, ...nodeDrawerProps } = WorkflowNodeDrawer.useProps();
|
||||
|
||||
const handleRollbackClick = () => {
|
||||
modal.confirm({
|
||||
title: t("workflow.detail.design.action.rollback.modal.title"),
|
||||
@@ -113,9 +116,9 @@ const WorkflowDetailDesign = () => {
|
||||
<WorkflowEditor
|
||||
ref={editorRef}
|
||||
initialData={editorData}
|
||||
onNodeClick={(ctx, node) => {
|
||||
console.log(ctx, node);
|
||||
console.log(editorRef.current);
|
||||
onNodeClick={(_, node) => {
|
||||
setDrawerNode(node);
|
||||
setNodeDrawerOpen(true);
|
||||
}}
|
||||
>
|
||||
<div className="absolute top-8 z-10 w-full px-4">
|
||||
@@ -155,7 +158,7 @@ const WorkflowDetailDesign = () => {
|
||||
<div className="absolute bottom-8 z-10 w-full px-4">
|
||||
<div className="container">
|
||||
<div className="flex justify-end">
|
||||
<WorkflowEditorToolbar
|
||||
<WorkflowToolbar
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
borderRadius: themeToken.borderRadius,
|
||||
@@ -164,6 +167,8 @@ const WorkflowDetailDesign = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WorkflowNodeDrawer {...nodeDrawerProps}>TODO:</WorkflowNodeDrawer>
|
||||
</WorkflowEditor>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user