Merge pull request #159 from SuanmoSuanyangTechnology/feature/ui_zy
Feature/UI zy
This commit is contained in:
13
web/src/utils/yamlExport.ts
Normal file
13
web/src/utils/yamlExport.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
|
||||||
|
export const exportToYaml = (data: unknown, filename: string = 'export.yaml') => {
|
||||||
|
const yamlStr = yaml.dump(data);
|
||||||
|
const blob = new Blob([yamlStr], { type: 'text/yaml' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
@@ -221,12 +221,12 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
saveAgentConfig(data.app_id, params)
|
saveAgentConfig(data.app_id, params)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
message.success(t('common.saveSuccess'))
|
message.success(t('common.saveSuccess'))
|
||||||
}
|
}
|
||||||
setIsSave(false)
|
setIsSave(false)
|
||||||
resolve(true)
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -58,16 +58,14 @@ const Cluster = forwardRef<ClusterRef>((_props, ref) => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('params', params)
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
form.validateFields().then(() => {
|
form.validateFields().then(() => {
|
||||||
saveMultiAgentConfig(id as string, params)
|
saveMultiAgentConfig(id as string, params)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
message.success(t('common.saveSuccess'))
|
message.success(t('common.saveSuccess'))
|
||||||
}
|
}
|
||||||
resolve(true)
|
resolve(res)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import exportIcon from '@/assets/images/export_hover.svg'
|
|||||||
import deleteIcon from '@/assets/images/delete_hover.svg'
|
import deleteIcon from '@/assets/images/delete_hover.svg'
|
||||||
import type { Application, ApplicationModalRef } from '@/views/ApplicationManagement/types';
|
import type { Application, ApplicationModalRef } from '@/views/ApplicationManagement/types';
|
||||||
import ApplicationModal from '@/views/ApplicationManagement/components/ApplicationModal'
|
import ApplicationModal from '@/views/ApplicationManagement/components/ApplicationModal'
|
||||||
import type { CopyModalRef, WorkflowRef } from '../types'
|
import type { CopyModalRef, AgentRef, ClusterRef, WorkflowRef } from '../types'
|
||||||
import { deleteApplication } from '@/api/application'
|
import { deleteApplication } from '@/api/application'
|
||||||
import CopyModal from './CopyModal'
|
import CopyModal from './CopyModal'
|
||||||
|
|
||||||
@@ -30,10 +30,11 @@ interface ConfigHeaderProps {
|
|||||||
handleChangeTab: (key: string) => void;
|
handleChangeTab: (key: string) => void;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
workflowRef: React.RefObject<WorkflowRef>
|
workflowRef: React.RefObject<WorkflowRef>
|
||||||
|
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
|
||||||
}
|
}
|
||||||
const ConfigHeader: FC<ConfigHeaderProps> = ({
|
const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||||
application, activeTab, handleChangeTab, refresh,
|
application, activeTab, handleChangeTab, refresh,
|
||||||
workflowRef
|
workflowRef,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -48,7 +49,7 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
const formatMenuItems = () => {
|
const formatMenuItems = () => {
|
||||||
const items = ['edit', 'copy', 'delete'].map(key => ({
|
const items = ['edit', 'copy', 'export', 'delete'].map(key => ({
|
||||||
key,
|
key,
|
||||||
icon: <img src={menuIcons[key]} className="rb:w-4 rb:h-4 rb:mr-2" />,
|
icon: <img src={menuIcons[key]} className="rb:w-4 rb:h-4 rb:mr-2" />,
|
||||||
label: t(`common.${key}`),
|
label: t(`common.${key}`),
|
||||||
@@ -59,7 +60,6 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleClick: MenuProps['onClick'] = ({ key }) => {
|
const handleClick: MenuProps['onClick'] = ({ key }) => {
|
||||||
console.log('key', key)
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'edit':
|
case 'edit':
|
||||||
applicationModalRef.current?.handleOpen(application as Application)
|
applicationModalRef.current?.handleOpen(application as Application)
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ const ApplicationConfig: React.FC = () => {
|
|||||||
handleChangeTab={handleChangeTab}
|
handleChangeTab={handleChangeTab}
|
||||||
application={application as Application}
|
application={application as Application}
|
||||||
refresh={getApplicationInfo}
|
refresh={getApplicationInfo}
|
||||||
|
appRef={application?.type === 'agent' ? agentRef : application?.type === 'multi_agent' ? clusterRef : application?.type === 'workflow' ? workflowRef : undefined}
|
||||||
workflowRef={workflowRef}
|
workflowRef={workflowRef}
|
||||||
/>
|
/>
|
||||||
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />}
|
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />}
|
||||||
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} application={application as Application} />}
|
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} />}
|
||||||
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />}
|
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />}
|
||||||
{activeTab === 'api' && <Api application={application} />}
|
{activeTab === 'api' && <Api application={application} />}
|
||||||
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}
|
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
|||||||
import { Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
||||||
import { nodeLibrary, graphNodeLibrary } from '../../constant';
|
import { nodeLibrary, graphNodeLibrary, edgeAttrs } from '../../constant';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
||||||
@@ -47,7 +47,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
graph.addEdge({
|
graph.addEdge({
|
||||||
source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() },
|
source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() },
|
||||||
target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' },
|
target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' },
|
||||||
attrs: edge.getAttrs(),
|
...edgeAttrs
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
graph.addEdge({
|
graph.addEdge({
|
||||||
source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' },
|
source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' },
|
||||||
target: { cell: edge.getTargetCellId(), port: targetPortId },
|
target: { cell: edge.getTargetCellId(), port: targetPortId },
|
||||||
attrs: edge.getAttrs(),
|
...edgeAttrs
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import { useEffect } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
||||||
import { graphNodeLibrary } from '../../constant';
|
import { graphNodeLibrary, edgeAttrs } from '../../constant';
|
||||||
|
|
||||||
import { edge_color } from '../../hooks/useWorkflowGraph'
|
|
||||||
|
|
||||||
const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
||||||
const data = node.getData() || {};
|
const data = node.getData() || {};
|
||||||
@@ -56,16 +54,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
graph.addEdge({
|
graph.addEdge({
|
||||||
source: { cell: cycleStartNode.id, port: sourcePort },
|
source: { cell: cycleStartNode.id, port: sourcePort },
|
||||||
target: { cell: addNode.id, port: targetPort },
|
target: { cell: addNode.id, port: targetPort },
|
||||||
attrs: {
|
...edgeAttrs,
|
||||||
line: {
|
|
||||||
stroke: edge_color,
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,16 +111,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
cell: addNode.id,
|
cell: addNode.id,
|
||||||
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
|
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
|
||||||
},
|
},
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: edge_color,
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
graph.addEdge(edgeConfig)
|
graph.addEdge(edgeConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { nodeLibrary, graphNodeLibrary } from '../constant';
|
import { nodeLibrary, graphNodeLibrary, edgeAttrs } from '../constant';
|
||||||
|
|
||||||
interface PortClickHandlerProps {
|
interface PortClickHandlerProps {
|
||||||
graph: any;
|
graph: any;
|
||||||
@@ -149,16 +149,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
|
|||||||
graph.addEdge({
|
graph.addEdge({
|
||||||
source: { cell: sourceNode.id, port: sourcePort },
|
source: { cell: sourceNode.id, port: sourcePort },
|
||||||
target: { cell: newNode.id, port: targetPort },
|
target: { cell: newNode.id, port: targetPort },
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: '#155EEF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0
|
// zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Form, Button, Select, Space, Divider, InputNumber, Radio, type SelectPr
|
|||||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
import VariableSelect from '../VariableSelect'
|
import VariableSelect from '../VariableSelect'
|
||||||
import Editor from '../../Editor'
|
import Editor from '../../Editor'
|
||||||
|
import { edgeAttrs } from '../../../constant'
|
||||||
|
|
||||||
interface CaseListProps {
|
interface CaseListProps {
|
||||||
value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>;
|
value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>;
|
||||||
@@ -120,16 +121,7 @@ const CaseList: FC<CaseListProps> = ({
|
|||||||
graphRef.current?.addEdge({
|
graphRef.current?.addEdge({
|
||||||
source: { cell: sourceCellId, port: sourcePortId },
|
source: { cell: sourceCellId, port: sourcePortId },
|
||||||
target: { cell: selectedNode.id, port: targetPortId },
|
target: { cell: selectedNode.id, port: targetPortId },
|
||||||
attrs: {
|
...edgeAttrs,
|
||||||
line: {
|
|
||||||
stroke: '#155EEF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
graphRef.current?.removeCell(edge);
|
graphRef.current?.removeCell(edge);
|
||||||
@@ -174,16 +166,7 @@ const CaseList: FC<CaseListProps> = ({
|
|||||||
graphRef.current?.addEdge({
|
graphRef.current?.addEdge({
|
||||||
source: { cell: selectedNode.id, port: newPortId },
|
source: { cell: selectedNode.id, port: newPortId },
|
||||||
target: { cell: targetCellId, port: targetPortId },
|
target: { cell: targetCellId, port: targetPortId },
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: '#155EEF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Graph, Node } from '@antv/x6';
|
|||||||
|
|
||||||
import Editor from '../../Editor';
|
import Editor from '../../Editor';
|
||||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
|
import { edgeAttrs } from '../../../constant'
|
||||||
|
|
||||||
interface CategoryListProps {
|
interface CategoryListProps {
|
||||||
parentName: string;
|
parentName: string;
|
||||||
@@ -70,16 +71,7 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
|||||||
graphRef.current?.addEdge({
|
graphRef.current?.addEdge({
|
||||||
source: { cell: sourceCellId, port: sourcePortId },
|
source: { cell: sourceCellId, port: sourcePortId },
|
||||||
target: { cell: selectedNode.id, port: targetPortId },
|
target: { cell: selectedNode.id, port: targetPortId },
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: '#155EEF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -110,16 +102,7 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
|||||||
graphRef.current?.addEdge({
|
graphRef.current?.addEdge({
|
||||||
source: { cell: selectedNode.id, port: newPortId },
|
source: { cell: selectedNode.id, port: newPortId },
|
||||||
target: { cell: targetCellId, port: targetPortId },
|
target: { cell: targetCellId, port: targetPortId },
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: '#155EEF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'block',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Form, Select, Input, Button } from 'antd'
|
import { Form, Select, Input, Button, InputNumber } from 'antd'
|
||||||
import VariableSelect from '../VariableSelect'
|
import VariableSelect from '../VariableSelect'
|
||||||
|
|
||||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
@@ -93,6 +93,7 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{fields.map(({ key, name }, index) => {
|
{fields.map(({ key, name }, index) => {
|
||||||
|
const currentType = value?.[index]?.type;
|
||||||
const currentInputType = value?.[index]?.input_type;
|
const currentInputType = value?.[index]?.input_type;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -131,7 +132,8 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Form.Item name={[name, 'value']} noStyle>
|
<Form.Item name={[name, 'value']} noStyle>
|
||||||
{currentInputType === 'variable' ? (
|
{currentInputType === 'variable'
|
||||||
|
? (
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={availableOptions.filter(option => {
|
options={availableOptions.filter(option => {
|
||||||
@@ -143,7 +145,15 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
|||||||
variant="borderless"
|
variant="borderless"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
) : (
|
)
|
||||||
|
: currentType === 'number'
|
||||||
|
? <InputNumber
|
||||||
|
placeholder={t('common.pleaseEnter')}
|
||||||
|
variant="borderless"
|
||||||
|
className="rb:w-full! rb:my-1!"
|
||||||
|
onChange={(value) => form.setFieldValue([name, 'value'], value)}
|
||||||
|
/>
|
||||||
|
: (
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
placeholder={t('common.pleaseEnter')}
|
placeholder={t('common.pleaseEnter')}
|
||||||
rows={3}
|
rows={3}
|
||||||
|
|||||||
@@ -517,6 +517,8 @@ interface NodeConfig {
|
|||||||
ports?: PortsConfig;
|
ports?: PortsConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const edge_color = '#155EEF';
|
||||||
|
export const edge_selected_color = '#4DA8FF'
|
||||||
// 统一的端口 markup 配置
|
// 统一的端口 markup 配置
|
||||||
export const portMarkup = [
|
export const portMarkup = [
|
||||||
{
|
{
|
||||||
@@ -534,9 +536,9 @@ export const portAttrs = {
|
|||||||
body: {
|
body: {
|
||||||
r: 6,
|
r: 6,
|
||||||
magnet: true,
|
magnet: true,
|
||||||
stroke: '#155EEF',
|
stroke: edge_color,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
fill: '#155EEF',
|
fill: edge_color,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
text: '+',
|
text: '+',
|
||||||
@@ -776,4 +778,18 @@ export const outputVariable: { [key: string]: OutputVariable } = {
|
|||||||
{ name: "output", type: "string" },
|
{ name: "output", type: "string" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const edgeAttrs = {
|
||||||
|
attrs: {
|
||||||
|
line: {
|
||||||
|
stroke: edge_color,
|
||||||
|
strokeWidth: 1,
|
||||||
|
targetMarker: {
|
||||||
|
name: 'block',
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import { App } from 'antd'
|
|||||||
import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '@antv/x6';
|
import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '@antv/x6';
|
||||||
import { register } from '@antv/x6-react-shape';
|
import { register } from '@antv/x6-react-shape';
|
||||||
|
|
||||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs } from '../constant';
|
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color } from '../constant';
|
||||||
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
||||||
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
||||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
||||||
@@ -23,12 +23,8 @@ export interface UseWorkflowGraphReturn {
|
|||||||
setSelectedNode: React.Dispatch<React.SetStateAction<Node | null>>;
|
setSelectedNode: React.Dispatch<React.SetStateAction<Node | null>>;
|
||||||
zoomLevel: number;
|
zoomLevel: number;
|
||||||
setZoomLevel: React.Dispatch<React.SetStateAction<number>>;
|
setZoomLevel: React.Dispatch<React.SetStateAction<number>>;
|
||||||
canUndo: boolean;
|
|
||||||
canRedo: boolean;
|
|
||||||
isHandMode: boolean;
|
isHandMode: boolean;
|
||||||
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
onUndo: () => void;
|
|
||||||
onRedo: () => void;
|
|
||||||
onDrop: (event: React.DragEvent) => void;
|
onDrop: (event: React.DragEvent) => void;
|
||||||
blankClick: () => void;
|
blankClick: () => void;
|
||||||
deleteEvent: () => boolean | void;
|
deleteEvent: () => boolean | void;
|
||||||
@@ -39,8 +35,6 @@ export interface UseWorkflowGraphReturn {
|
|||||||
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const edge_color = '#155EEF';
|
|
||||||
const edge_selected_color = '#4DA8FF'
|
|
||||||
export const useWorkflowGraph = ({
|
export const useWorkflowGraph = ({
|
||||||
containerRef,
|
containerRef,
|
||||||
miniMapRef,
|
miniMapRef,
|
||||||
@@ -51,9 +45,6 @@ export const useWorkflowGraph = ({
|
|||||||
const graphRef = useRef<Graph>();
|
const graphRef = useRef<Graph>();
|
||||||
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
|
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
|
||||||
const [zoomLevel, setZoomLevel] = useState(1);
|
const [zoomLevel, setZoomLevel] = useState(1);
|
||||||
const historyRef = useRef<{ undoStack: string[], redoStack: string[] }>({ undoStack: [], redoStack: [] });
|
|
||||||
const [canUndo, setCanUndo] = useState(false);
|
|
||||||
const [canRedo, setCanRedo] = useState(false);
|
|
||||||
const [isHandMode, setIsHandMode] = useState(true);
|
const [isHandMode, setIsHandMode] = useState(true);
|
||||||
const [config, setConfig] = useState<WorkflowConfig | null>(null);
|
const [config, setConfig] = useState<WorkflowConfig | null>(null);
|
||||||
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
|
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
|
||||||
@@ -338,17 +329,7 @@ export const useWorkflowGraph = ({
|
|||||||
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
|
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
|
||||||
},
|
},
|
||||||
connector: { name: 'smooth' },
|
connector: { name: 'smooth' },
|
||||||
attrs: {
|
...edgeAttrs
|
||||||
line: {
|
|
||||||
stroke: edge_color,
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'diamond',
|
|
||||||
width: 4,
|
|
||||||
height: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// zIndex: loopIterationCount
|
// zIndex: loopIterationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,48 +349,6 @@ export const useWorkflowGraph = ({
|
|||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveState = () => {
|
|
||||||
if (!graphRef.current) return;
|
|
||||||
const state = JSON.stringify(graphRef.current.toJSON());
|
|
||||||
historyRef.current.undoStack.push(state);
|
|
||||||
historyRef.current.redoStack = [];
|
|
||||||
if (historyRef.current.undoStack.length > 50) {
|
|
||||||
historyRef.current.undoStack.shift();
|
|
||||||
}
|
|
||||||
updateHistoryState();
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateHistoryState = () => {
|
|
||||||
setCanUndo(historyRef.current.undoStack.length > 1);
|
|
||||||
setCanRedo(historyRef.current.redoStack.length > 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 撤销
|
|
||||||
const onUndo = () => {
|
|
||||||
if (!graphRef.current || historyRef.current.undoStack.length === 0) return;
|
|
||||||
const { undoStack = [], redoStack = [] } = historyRef.current
|
|
||||||
|
|
||||||
const currentState = JSON.stringify(graphRef.current.toJSON());
|
|
||||||
const prevState = undoStack[undoStack.length - 2];
|
|
||||||
|
|
||||||
historyRef.current.redoStack = [...redoStack, currentState]
|
|
||||||
historyRef.current.undoStack = undoStack.slice(0, undoStack.length - 1)
|
|
||||||
graphRef.current.fromJSON(JSON.parse(prevState));
|
|
||||||
updateHistoryState();
|
|
||||||
};
|
|
||||||
// 重做
|
|
||||||
const onRedo = () => {
|
|
||||||
if (!graphRef.current || historyRef.current.redoStack.length === 0) return;
|
|
||||||
const { undoStack = [], redoStack = [] } = historyRef.current
|
|
||||||
|
|
||||||
const nextState = redoStack[redoStack.length - 1];
|
|
||||||
|
|
||||||
historyRef.current.undoStack = [...undoStack, nextState]
|
|
||||||
historyRef.current.redoStack = redoStack.slice(0, redoStack.length - 1)
|
|
||||||
graphRef.current.fromJSON(JSON.parse(nextState));
|
|
||||||
updateHistoryState();
|
|
||||||
};
|
|
||||||
// 使用插件
|
// 使用插件
|
||||||
const setupPlugins = () => {
|
const setupPlugins = () => {
|
||||||
if (!graphRef.current || !miniMapRef.current) return;
|
if (!graphRef.current || !miniMapRef.current) return;
|
||||||
@@ -563,20 +502,6 @@ export const useWorkflowGraph = ({
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// 撤销快捷键事件
|
|
||||||
const undoEvent = () => {
|
|
||||||
if (canUndo) {
|
|
||||||
onUndo();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// 重做快捷键事件
|
|
||||||
const redoEvent = () => {
|
|
||||||
if (canRedo) {
|
|
||||||
onRedo();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// 删除选中的节点和连线事件
|
// 删除选中的节点和连线事件
|
||||||
const deleteEvent = () => {
|
const deleteEvent = () => {
|
||||||
if (!graphRef.current) return;
|
if (!graphRef.current) return;
|
||||||
@@ -748,8 +673,6 @@ export const useWorkflowGraph = ({
|
|||||||
background: {
|
background: {
|
||||||
color: '#F0F3F8',
|
color: '#F0F3F8',
|
||||||
},
|
},
|
||||||
// width: container.clientWidth || 800,
|
|
||||||
// height: container.clientHeight || 600,
|
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
grid: {
|
grid: {
|
||||||
visible: true,
|
visible: true,
|
||||||
@@ -765,37 +688,26 @@ export const useWorkflowGraph = ({
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
connecting: {
|
connecting: {
|
||||||
// router: 'orth',
|
|
||||||
// router: 'manhattan',
|
|
||||||
connector: {
|
connector: {
|
||||||
name: 'smooth',
|
name: 'smooth',
|
||||||
args: {
|
args: {
|
||||||
radius: 8,
|
radius: 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
anchor: 'center',
|
anchor: 'midSide',
|
||||||
connectionPoint: 'anchor',
|
connectionPoint: 'anchor',
|
||||||
allowBlank: false,
|
allowBlank: false,
|
||||||
|
allowLoop: false,
|
||||||
allowNode: false,
|
allowNode: false,
|
||||||
allowEdge: false,
|
allowEdge: false,
|
||||||
|
allowPort: true,
|
||||||
|
allowMulti: true,
|
||||||
highlight: true,
|
highlight: true,
|
||||||
snap: {
|
snap: {
|
||||||
radius: 20,
|
radius: 20,
|
||||||
},
|
},
|
||||||
createEdge() {
|
createEdge() {
|
||||||
return graphRef.current?.createEdge({
|
return graphRef.current?.createEdge(edgeAttrs);
|
||||||
attrs: {
|
|
||||||
line: {
|
|
||||||
stroke: edge_color,
|
|
||||||
strokeWidth: 1,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'diamond',
|
|
||||||
width: 4,
|
|
||||||
height: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
validateConnection({ sourceCell, targetCell, targetMagnet }) {
|
validateConnection({ sourceCell, targetCell, targetMagnet }) {
|
||||||
if (!targetMagnet) return false;
|
if (!targetMagnet) return false;
|
||||||
@@ -901,27 +813,7 @@ export const useWorkflowGraph = ({
|
|||||||
// 监听缩放事件
|
// 监听缩放事件
|
||||||
graphRef.current.on('scale', scaleEvent);
|
graphRef.current.on('scale', scaleEvent);
|
||||||
// 监听节点移动事件
|
// 监听节点移动事件
|
||||||
// graphRef.current.on('node:moved', nodeMoved);
|
graphRef.current.on('node:moved', nodeMoved);
|
||||||
graphRef.current.on('node:change:position', nodeChangePosition);
|
|
||||||
|
|
||||||
// 监听画布变化事件
|
|
||||||
const events = [
|
|
||||||
'node:added',
|
|
||||||
'node:removed',
|
|
||||||
'edge:added',
|
|
||||||
'edge:removed',
|
|
||||||
];
|
|
||||||
events.forEach(event => {
|
|
||||||
graphRef.current!.on(event, () => {
|
|
||||||
console.log('event', event);
|
|
||||||
setTimeout(() => saveState(), 50);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听撤销键盘事件
|
|
||||||
graphRef.current.bindKey(['ctrl+z', 'cmd+z'], undoEvent);
|
|
||||||
// 监听重做键盘事件
|
|
||||||
graphRef.current.bindKey(['ctrl+shift+z', 'cmd+shift+z', 'ctrl+y', 'cmd+y'], redoEvent);
|
|
||||||
// 监听复制键盘事件
|
// 监听复制键盘事件
|
||||||
graphRef.current.bindKey(['ctrl+c', 'cmd+c'], copyEvent);
|
graphRef.current.bindKey(['ctrl+c', 'cmd+c'], copyEvent);
|
||||||
// 监听粘贴键盘事件
|
// 监听粘贴键盘事件
|
||||||
@@ -929,11 +821,6 @@ export const useWorkflowGraph = ({
|
|||||||
// 删除选中的节点和连线
|
// 删除选中的节点和连线
|
||||||
graphRef.current.bindKey(['ctrl+d', 'cmd+d', 'delete', 'backspace'], deleteEvent);
|
graphRef.current.bindKey(['ctrl+d', 'cmd+d', 'delete', 'backspace'], deleteEvent);
|
||||||
|
|
||||||
// 保存初始状态
|
|
||||||
setTimeout(() => saveState(), 100);
|
|
||||||
// init window hook
|
|
||||||
(window as Window & { __x6_instances__?: Graph[] }).__x6_instances__ = [];
|
|
||||||
(window as Window & { __x6_instances__?: Graph[] }).__x6_instances__?.push(graphRef.current);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1146,11 +1033,11 @@ export const useWorkflowGraph = ({
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
saveWorkflowConfig(config.app_id, params as WorkflowConfig)
|
saveWorkflowConfig(config.app_id, params as WorkflowConfig)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
message.success(t('common.saveSuccess'))
|
message.success(t('common.saveSuccess'))
|
||||||
}
|
}
|
||||||
resolve(true)
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
@@ -1165,12 +1052,8 @@ export const useWorkflowGraph = ({
|
|||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
zoomLevel,
|
zoomLevel,
|
||||||
setZoomLevel,
|
setZoomLevel,
|
||||||
canUndo,
|
|
||||||
canRedo,
|
|
||||||
isHandMode,
|
isHandMode,
|
||||||
setIsHandMode,
|
setIsHandMode,
|
||||||
onUndo,
|
|
||||||
onRedo,
|
|
||||||
onDrop,
|
onDrop,
|
||||||
blankClick,
|
blankClick,
|
||||||
deleteEvent,
|
deleteEvent,
|
||||||
|
|||||||
Reference in New Issue
Block a user