feat(ui): add default workflow designer layout setting

This commit is contained in:
Fu Diwei
2025-10-15 17:24:15 +08:00
parent b374e7299e
commit 06e92c6bc0
6 changed files with 81 additions and 10 deletions
@@ -4,12 +4,12 @@ import {
EditorRenderer,
EditorState,
FixedLayoutEditorProvider,
FlowLayoutDefault,
type FixedLayoutPluginContext,
type FixedLayoutProps,
type FlowDocumentJSON,
type FlowNodeEntity,
FlowTextKey,
getNodeForm,
} from "@flowgram.ai/fixed-layout-editor";
import { createMinimapPlugin } from "@flowgram.ai/minimap-plugin";
import "@flowgram.ai/fixed-layout-editor/index.css";
@@ -139,9 +139,11 @@ const Designer = forwardRef<DesignerInstance, DesignerProps>(
rendered.current = true;
// 画布初始化后向下滚动一点,露出可能被 Alert 遮挡的部分
setTimeout(() => {
ctx.playground.config.scroll({ scrollY: -80 });
}, 1);
if (defaultLayout === FlowLayoutDefault.VERTICAL_FIXED_LAYOUT) {
setTimeout(() => {
ctx.playground.config.scroll({ scrollY: -80 });
}, 1);
}
},
}),
[defaultEditorState, defaultLayout, initialData, readonly, onDocumentChange, themeToken]
@@ -195,12 +197,12 @@ const Designer = forwardRef<DesignerInstance, DesignerProps>(
node = flowgramEditorRef.current!.document.getNode(node)!;
}
const form = getNodeForm(node);
const form = node.form;
return form ? form.validate().then((res) => res && !form.state.invalid) : Promise.resolve(true);
},
validateAllNodes() {
const nodes = flowgramEditorRef.current!.document.getAllNodes();
const forms = nodes.map((node) => getNodeForm(node)).filter((form) => form != null);
const forms = nodes.map((node) => node.form).filter((form) => form != null);
return Promise.allSettled(forms.map((form) => form.validate())).then((res) => forms.every((form, index) => res[index] && !form.state.invalid));
},
};
+8 -1
View File
@@ -7,8 +7,14 @@ if (!localStorage.getItem(LOCAL_STORAGE_KEY)) {
}
type AppSettings = {
// 每页显示的默认条目数
/**
* 每页显示的默认条目数。
*/
defaultPerPage?: number;
/**
* 工作流默认布局。
*/
defaultWorkflowLayout?: "horizontal" | "vertical";
};
export type UseAppSettingsReturns = {
@@ -26,6 +32,7 @@ const useAppSettings = (): UseAppSettingsReturns => {
defaultValue: {},
});
state.defaultPerPage ??= 15;
state.defaultWorkflowLayout ??= "vertical";
const resetState = useCallback(() => {
localStorage.removeItem(LOCAL_STORAGE_KEY);
+5
View File
@@ -29,6 +29,11 @@
"settings.appearance.pagination.form.default_per_page.label": "Default items per page",
"settings.appearance.pagination.form.default_per_page.placeholder": "Please enter default items per page",
"settings.appearance.pagination.form.default_per_page.unit": "items per page",
"settings.appearance.workflow.title": "Workflow",
"settings.appearance.workflow.form.default_designer_layout.label": "Default designer layout",
"settings.appearance.workflow.form.default_designer_layout.placeholder": "Please select default designer layout",
"settings.appearance.workflow.form.default_designer_layout.option.horizontal": "Horizontal layout",
"settings.appearance.workflow.form.default_designer_layout.option.vertical": "Vertical layout",
"settings.sslprovider.tab": "Certificate authority",
"settings.sslprovider.ca.title": "System-wide CA",
+5
View File
@@ -29,6 +29,11 @@
"settings.appearance.pagination.form.default_per_page.label": "列表页默认显示数量",
"settings.appearance.pagination.form.default_per_page.placeholder": "请输入列表页默认显示数量",
"settings.appearance.pagination.form.default_per_page.unit": "条每页",
"settings.appearance.workflow.title": "工作流",
"settings.appearance.workflow.form.default_designer_layout.label": "编辑器默认布局",
"settings.appearance.workflow.form.default_designer_layout.placeholder": "请选择编辑器默认布局",
"settings.appearance.workflow.form.default_designer_layout.option.horizontal": "水平布局",
"settings.appearance.workflow.form.default_designer_layout.option.vertical": "垂直布局",
"settings.sslprovider.tab": "证书颁发机构",
"settings.sslprovider.ca.title": "全局证书颁发机构",
+39 -1
View File
@@ -24,6 +24,11 @@ const SettingsAppearance = () => {
<h2>{t("settings.appearance.pagination.title")}</h2>
<SettingsAppearancePagination className="md:max-w-160" />
<Divider />
<h2>{t("settings.appearance.workflow.title")}</h2>
<SettingsAppearanceWorkflow className="md:max-w-160" />
</>
);
};
@@ -105,7 +110,7 @@ const SettingsAppearancePagination = ({ className, style }: { className?: string
const { appSettings: globalAppSettings, setAppSettings: setGlobalAppSettings } = useAppSettings();
const handleChange = (value: number) => {
const handleChange = (value: (typeof globalAppSettings)["defaultPerPage"]) => {
setGlobalAppSettings(
produce(globalAppSettings, (draft) => {
draft.defaultPerPage = value;
@@ -133,4 +138,37 @@ const SettingsAppearancePagination = ({ className, style }: { className?: string
);
};
const SettingsAppearanceWorkflow = ({ className, style }: { className?: string; style?: React.CSSProperties }) => {
const { t } = useTranslation();
const { appSettings: globalAppSettings, setAppSettings: setGlobalAppSettings } = useAppSettings();
const handleChange = (value: (typeof globalAppSettings)["defaultWorkflowLayout"]) => {
setGlobalAppSettings(
produce(globalAppSettings, (draft) => {
draft.defaultWorkflowLayout = value;
})
);
};
return (
<div className={className} style={style}>
<Form layout="vertical">
<Form.Item label={t("settings.appearance.workflow.form.default_designer_layout.label")}>
<Select
options={["horizontal", "vertical"].map((value) => ({
key: value,
value: value,
label: t(`settings.appearance.workflow.form.default_designer_layout.option.${value}`),
}))}
placeholder={t("settings.appearance.workflow.form.default_designer_layout.placeholder")}
defaultValue={globalAppSettings.defaultWorkflowLayout}
onChange={handleChange}
/>
</Form.Item>
</Form>
</div>
);
};
export default SettingsAppearance;
@@ -1,5 +1,6 @@
import { useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { FlowLayoutDefault } from "@flowgram.ai/fixed-layout-editor";
import { IconArrowBackUp, IconDots, IconTransferIn, IconTransferOut } from "@tabler/icons-react";
import { useDeepCompareEffect } from "ahooks";
import { Alert, App, Button, Card, Dropdown, Result, Space, theme } from "antd";
@@ -9,7 +10,7 @@ import Show from "@/components/Show";
import { WorkflowDesigner, type WorkflowDesignerInstance, WorkflowNodeDrawer, WorkflowToolbar } from "@/components/workflow/designer";
import WorkflowGraphExportModal from "@/components/workflow/WorkflowGraphExportModal";
import WorkflowGraphImportModal from "@/components/workflow/WorkflowGraphImportModal";
import { useZustandShallowSelector } from "@/hooks";
import { useAppSettings, useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import { getErrMsg } from "@/utils/error";
@@ -19,6 +20,8 @@ const WorkflowDetailDesign = () => {
const { token: themeToken } = theme.useToken();
const { message, modal, notification } = App.useApp();
const { appSettings: globalAppSettings } = useAppSettings();
const { workflow, ...workflowStore } = useWorkflowStore(useZustandShallowSelector(["workflow", "orchestrate", "publish", "rollback"]));
const workflowRollbackDisabled = useMemo(() => !workflow.hasDraft || !workflow.hasContent, [workflow.hasDraft, workflow.hasContent]);
@@ -134,7 +137,18 @@ const WorkflowDetailDesign = () => {
},
}}
>
<WorkflowDesigner ref={designerRef} onDocumentChange={handleDesignerDocumentChange} onNodeClick={(_, node) => designerNodeDrawer.open(node)}>
<WorkflowDesigner
ref={designerRef}
defaultLayout={
globalAppSettings.defaultWorkflowLayout === "horizontal"
? FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT
: globalAppSettings.defaultWorkflowLayout === "vertical"
? FlowLayoutDefault.VERTICAL_FIXED_LAYOUT
: void 0
}
onDocumentChange={handleDesignerDocumentChange}
onNodeClick={(_, node) => designerNodeDrawer.open(node)}
>
<div className="absolute top-8 z-10 w-full px-4">
<div className="container">
<div className="flex items-center justify-end gap-4">