Commit a26ce2a96b852ee47987e203a1e711d013a613e1

Authored by ww
1 parent 84825d99

feat: 选择创建规则链

1 import { Ref, toRaw, unref } from 'vue'; 1 import { Ref, toRaw, unref } from 'vue';
2 -import { BasicNodeBindData, NodeData } from '../types/node';  
3 -import { Elements, GraphNode } from '@vue-flow/core';  
4 -import { RuleChainType } from '../types/ruleNode'; 2 +import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';
  3 +import { Elements, GraphEdge, GraphNode } from '@vue-flow/core';
  4 +import { ConnectionItemType, RuleChainType } from '../types/ruleNode';
5 import { allComponents } from '../packages'; 5 import { allComponents } from '../packages';
6 import { RuleNodeTypeEnum } from '../packages/index.type'; 6 import { RuleNodeTypeEnum } from '../packages/index.type';
7 import { buildUUID } from '/@/utils/uuid'; 7 import { buildUUID } from '/@/utils/uuid';
@@ -9,6 +9,9 @@ import { isNullOrUnDef } from '/@/utils/is'; @@ -9,6 +9,9 @@ import { isNullOrUnDef } from '/@/utils/is';
9 import { useAddNodes } from './useAddNodes'; 9 import { useAddNodes } from './useAddNodes';
10 import { useAddEdges } from './useAddEdges'; 10 import { useAddEdges } from './useAddEdges';
11 import { RuleChainEntityType } from '../enum/entity'; 11 import { RuleChainEntityType } from '../enum/entity';
  12 +import { EntryCategoryComponentEnum } from '../enum/category';
  13 +
  14 +const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
12 15
13 export function useBasicDataTransform() { 16 export function useBasicDataTransform() {
14 const nodeConfigMap = new Map<string, NodeData>(); 17 const nodeConfigMap = new Map<string, NodeData>();
@@ -24,7 +27,7 @@ export function useBasicDataTransform() { @@ -24,7 +27,7 @@ export function useBasicDataTransform() {
24 } 27 }
25 28
26 function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) { 29 function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) {
27 - const { x: layoutX, y: layoutY } = node.computedPosition; 30 + const { x: layoutX, y: layoutY } = node.position;
28 31
29 return { 32 return {
30 debugMode: !!data?.debugMode, 33 debugMode: !!data?.debugMode,
@@ -172,8 +175,126 @@ export function useBasicDataTransform() { @@ -172,8 +175,126 @@ export function useBasicDataTransform() {
172 175
173 initNodeConfigMap(); 176 initNodeConfigMap();
174 177
  178 + /**
  179 + * @description 保存连接信息
  180 + */
  181 + function getConnections(
  182 + nodesRef: Ref<GraphNode[]> | GraphNode[],
  183 + edges: Ref<GraphEdge[]> | GraphEdge[]
  184 + ) {
  185 + const nodeIndexMap = new Map();
  186 +
  187 + const connections: ConnectionItemType[] = [];
  188 +
  189 + unref(nodesRef).forEach((item, index) => {
  190 + nodeIndexMap.set(item.id, index);
  191 + });
  192 +
  193 + for (const item of unref(edges)) {
  194 + const { data, target, source } = item;
  195 + const { data: bindData } = data as EdgeData;
  196 + const { type } = bindData || {};
  197 + const fromIndex = nodeIndexMap.get(source);
  198 + const toIndex = nodeIndexMap.get(target);
  199 + type?.forEach((key) => {
  200 + connections.push({ fromIndex, toIndex, type: key });
  201 + });
  202 + }
  203 +
  204 + return connections;
  205 + }
  206 +
  207 + function getNodes(nodesRef: Ref<GraphNode[]> | GraphNode[], removeId: boolean) {
  208 + const nodes: BasicNodeBindData[] = [];
  209 +
  210 + for (const node of unref(nodesRef)) {
  211 + const nodeData = node.data as NodeData;
  212 +
  213 + if (ignoreNodeKeys.includes(nodeData.config?.key as string)) continue;
  214 +
  215 + const data = nodeData.data;
  216 +
  217 + nodes.push(
  218 + Object.assign(
  219 + mergeData(data, nodeData, node),
  220 + nodeData.created && !removeId
  221 + ? ({
  222 + id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },
  223 + } as BasicNodeBindData)
  224 + : {}
  225 + )
  226 + );
  227 + }
  228 +
  229 + return nodes;
  230 + }
  231 +
  232 + function getFirsetNodeIndex(
  233 + nodesRef: Ref<GraphNode[]> | GraphNode[],
  234 + edges: Ref<GraphEdge[]> | GraphEdge[]
  235 + ) {
  236 + const inputNode = unref(edges).find(
  237 + (item) => (item.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT
  238 + );
  239 +
  240 + if (inputNode) {
  241 + const targetId = inputNode.target;
  242 + const index = unref(nodesRef).findIndex((item) => item.id === targetId);
  243 + return index;
  244 + }
  245 + }
  246 +
  247 + function combineData(
  248 + nodesRef: Ref<GraphNode[]> | GraphNode[] = [],
  249 + edgesRef: Ref<GraphEdge[]> | GraphEdge[] = [],
  250 + removeId = false
  251 + ) {
  252 + const extraIgnoreNodeRef = unref(nodesRef).filter(
  253 + (item) => !ignoreNodeKeys.includes((item.data as NodeData).config?.key as string)
  254 + );
  255 +
  256 + const connections = getConnections(extraIgnoreNodeRef, edgesRef);
  257 +
  258 + const nodes = getNodes(extraIgnoreNodeRef, removeId);
  259 +
  260 + const firstNodeIndex = getFirsetNodeIndex(extraIgnoreNodeRef, edgesRef);
  261 +
  262 + return { connections, nodes, firstNodeIndex };
  263 + }
  264 +
  265 + function validateCanCreateRuleChain(
  266 + nodes: Ref<GraphNode[]> | GraphNode[] = [],
  267 + edges: Ref<GraphEdge[]> | GraphEdge[] = []
  268 + ) {
  269 + const rootNode: GraphNode[] = [];
  270 +
  271 + let flag = true;
  272 + for (const node of unref(nodes)) {
  273 + const list = unref(edges).filter(
  274 + (edge) => edge.source === node.id || edge.target === node.id
  275 + );
  276 +
  277 + if (!list.length) {
  278 + flag = false;
  279 + break;
  280 + }
  281 +
  282 + if (!list.filter((edge) => edge.target === node.id).length) {
  283 + if (!rootNode.length) rootNode.push(node);
  284 + else {
  285 + flag = false;
  286 + break;
  287 + }
  288 + }
  289 + }
  290 +
  291 + return { flag, firstNode: rootNode[0] || null };
  292 + }
  293 +
175 return { 294 return {
176 mergeData, 295 mergeData,
  296 + combineData,
177 deconstructionData, 297 deconstructionData,
  298 + validateCanCreateRuleChain,
178 }; 299 };
179 } 300 }
@@ -3,11 +3,19 @@ import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache'; @@ -3,11 +3,19 @@ import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache';
3 import { RuleContextMenuEnum } from './useRuleChainContextMenu'; 3 import { RuleContextMenuEnum } from './useRuleChainContextMenu';
4 import { useAddNodes } from './useAddNodes'; 4 import { useAddNodes } from './useAddNodes';
5 import { buildUUID } from '/@/utils/uuid'; 5 import { buildUUID } from '/@/utils/uuid';
6 -import { toRaw, unref } from 'vue'; 6 +import { Ref, toRaw, unref } from 'vue';
7 import { useSaveAndRedo } from './useSaveAndRedo'; 7 import { useSaveAndRedo } from './useSaveAndRedo';
8 import { isUnDef } from '/@/utils/is'; 8 import { isUnDef } from '/@/utils/is';
9 import { useAddEdges } from './useAddEdges'; 9 import { useAddEdges } from './useAddEdges';
10 import { EdgeData } from '../types/node'; 10 import { EdgeData } from '../types/node';
  11 +import { CreateNodeModal } from '../src/components/CreateNodeModal';
  12 +import { CreateEdgeModal } from '../src/components/CreateEdgeModal';
  13 +import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
  14 +import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  15 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
  16 +import { useBasicDataTransform } from './useBasicDataTransform';
  17 +import { cloneDeep } from 'lodash-es';
  18 +import { useNewNode } from './useNewNode';
11 19
12 interface HandleContextMenuActionParamsType { 20 interface HandleContextMenuActionParamsType {
13 menuType: RuleContextMenuEnum; 21 menuType: RuleContextMenuEnum;
@@ -16,18 +24,58 @@ interface HandleContextMenuActionParamsType { @@ -16,18 +24,58 @@ interface HandleContextMenuActionParamsType {
16 node?: GraphNode; 24 node?: GraphNode;
17 edge?: GraphEdge; 25 edge?: GraphEdge;
18 useSaveAndRedoActionType?: ReturnType<typeof useSaveAndRedo>; 26 useSaveAndRedoActionType?: ReturnType<typeof useSaveAndRedo>;
  27 + modalActionType: {
  28 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  29 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  30 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  31 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  32 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  33 + };
  34 +}
  35 +
  36 +export function transformToRuleChain(
  37 + nodesRef: Ref<GraphNode[]> | GraphNode[] = [],
  38 + edgesRef: Ref<GraphEdge[]> | GraphEdge[] = []
  39 +) {
  40 + const nodeMap = new Map<string, GraphNode>();
  41 + const { combineData, validateCanCreateRuleChain } = useBasicDataTransform();
  42 +
  43 + nodesRef = cloneDeep(unref(nodesRef));
  44 + edgesRef = cloneDeep(unref(edgesRef));
  45 +
  46 + unref(nodesRef).forEach((node) => nodeMap.set(node.id, node));
  47 + const outputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.target));
  48 + const outputEdgesId = outputEdges.map((edge) => edge.id);
  49 +
  50 + const { getOutputNodeConfig } = useNewNode();
  51 + const outputNode = outputEdges.map((edge) => {
  52 + const id = buildUUID();
  53 + const name = (edge.data as EdgeData).data?.type?.join(' / ') || '';
  54 + edge.target = id;
  55 + return getOutputNodeConfig(name, edge.targetNode.position, id);
  56 + });
  57 +
  58 + nodesRef = [...nodesRef, ...(outputNode as GraphNode[])];
  59 +
  60 + const { connections, nodes } = combineData(nodesRef, edgesRef, true);
  61 +
  62 + const { firstNode } = validateCanCreateRuleChain(nodesRef, edgesRef);
  63 +
  64 + const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode.id);
  65 +
  66 + return { connections, nodes, firstNodeIndex, outputEdgesId };
19 } 67 }
20 68
21 export const NODE_WIDTH = 176; 69 export const NODE_WIDTH = 176;
22 export const NODE_HEIGHT = 48; 70 export const NODE_HEIGHT = 48;
23 71
24 -function getElementsCenter(nodes: GraphNode[]) { 72 +function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
25 let leftTopX: number | undefined; 73 let leftTopX: number | undefined;
26 let leftTopY: number | undefined; 74 let leftTopY: number | undefined;
27 let rightBottomX: number | undefined; 75 let rightBottomX: number | undefined;
28 let rightBottomY: number | undefined; 76 let rightBottomY: number | undefined;
29 77
30 - for (const node of nodes) { 78 + for (const node of unref(nodes)) {
31 const { position } = node; 79 const { position } = node;
32 const { x, y } = position; 80 const { x, y } = position;
33 if (isUnDef(leftTopX)) { 81 if (isUnDef(leftTopX)) {
@@ -200,6 +248,37 @@ export function useContextMenuAction() { @@ -200,6 +248,37 @@ export function useContextMenuAction() {
200 useSaveAndRedoActionType?.handleRedoChange(flowActionType!); 248 useSaveAndRedoActionType?.handleRedoChange(flowActionType!);
201 }; 249 };
202 250
  251 + const createRuleChain = async (_params: HandleContextMenuActionParamsType) => {
  252 + // const { useSaveAndRedoActionType, modalActionType, flowActionType } = params;
  253 + // const { createRuleChainModalActionType } = modalActionType;
  254 + // const result = (await unref(createRuleChainModalActionType)?.openCreateRuleChainModal()) as {
  255 + // name: string;
  256 + // additionalInfo: { description: string };
  257 + // };
  258 + // const ruleChainDetail = await saveRuleChainDetail(
  259 + // Object.assign(result, { debugger: false, type: 'CORE' }) as Partial<RuleChainDetail>
  260 + // );
  261 + // const selectedNodes = unref(flowActionType?.getSelectedNodes);
  262 + // const selectedEdges = unref(flowActionType?.getSelectedEdges);
  263 + // const { firstNodeIndex, connections, nodes, outputEdgesId } = transformToRuleChain(
  264 + // selectedNodes,
  265 + // selectedEdges
  266 + // );
  267 + // await saveRuleChainData({
  268 + // firstNodeIndex,
  269 + // connections,
  270 + // nodes,
  271 + // ruleChainId: ruleChainDetail.id,
  272 + // });
  273 + // const outputEdges = outputEdgesId.map((id) => flowActionType?.findEdge(id));
  274 + // console.log(getElementsCenter(unref(selectedNodes)));
  275 + // const { originX, originY } = getElementsCenter(unref(selectedNodes));
  276 + // const {} = useNewNode();
  277 + // flowActionType?.removeNodes(selectedNodes || []);
  278 + // flowActionType?.removeEdges(selectedEdges || []);
  279 + // useSaveAndRedoActionType?.triggerChange();
  280 + };
  281 +
203 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => { 282 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
204 const { menuType } = params; 283 const { menuType } = params;
205 284
@@ -213,6 +292,7 @@ export function useContextMenuAction() { @@ -213,6 +292,7 @@ export function useContextMenuAction() {
213 [RuleContextMenuEnum.SELECT_COPY]: selectCopy, 292 [RuleContextMenuEnum.SELECT_COPY]: selectCopy,
214 [RuleContextMenuEnum.APPLY_CHANGE]: applyChange, 293 [RuleContextMenuEnum.APPLY_CHANGE]: applyChange,
215 [RuleContextMenuEnum.UNDO_CHANGE]: undoChange, 294 [RuleContextMenuEnum.UNDO_CHANGE]: undoChange,
  295 + [RuleContextMenuEnum.CREATE_RULE_CHAIN]: createRuleChain,
216 }; 296 };
217 297
218 if (handlerMapping[menuType]) { 298 if (handlerMapping[menuType]) {
@@ -5,6 +5,7 @@ import type { CreateNodeModal } from '../src/components/CreateNodeModal'; @@ -5,6 +5,7 @@ import type { CreateNodeModal } from '../src/components/CreateNodeModal';
5 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal'; 5 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
6 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer'; 6 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
7 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer'; 7 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  8 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
8 9
9 const SYMBOL = Symbol('flow-context'); 10 const SYMBOL = Symbol('flow-context');
10 11
@@ -30,6 +31,11 @@ interface FlowContextOptionsType { @@ -30,6 +31,11 @@ interface FlowContextOptionsType {
30 updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>; 31 updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
31 32
32 /** 33 /**
  34 + * @description 创建规则链 actions
  35 + */
  36 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  37 +
  38 + /**
33 * @description vue flow store 39 * @description vue flow store
34 */ 40 */
35 flowActionType: VueFlowStore; 41 flowActionType: VueFlowStore;
src/views/rule/designer/hook/useNewNode.ts renamed from src/views/rule/designer/hook/useInputNode.ts
  1 +import { XYPosition } from '@vue-flow/core';
1 import { Config as InputConfig } from '../packages/Entry/Input/config'; 2 import { Config as InputConfig } from '../packages/Entry/Input/config';
  3 +import { Config as OutputConfig } from '../packages/Flow/Output/config';
  4 +import { Config as RuleChainConfig } from '../packages/Flow/RuleChain/config';
2 import { useAddNodes } from './useAddNodes'; 5 import { useAddNodes } from './useAddNodes';
3 6
4 -export function useInputNode() { 7 +export function useNewNode() {
5 const getInputNodeConfig = (id?: string) => { 8 const getInputNodeConfig = (id?: string) => {
6 const { getAddNodesParams } = useAddNodes(); 9 const { getAddNodesParams } = useAddNodes();
7 10
@@ -20,5 +23,38 @@ export function useInputNode() { @@ -20,5 +23,38 @@ export function useInputNode() {
20 return newNode; 23 return newNode;
21 }; 24 };
22 25
23 - return { getInputNodeConfig }; 26 + const getOutputNodeConfig = (name: string, position: XYPosition, id?: string) => {
  27 + const { getAddNodesParams } = useAddNodes();
  28 +
  29 + const newNode = getAddNodesParams(
  30 + position,
  31 + {
  32 + ...new OutputConfig(),
  33 + data: {
  34 + name,
  35 + },
  36 + },
  37 + { id, draggable: false, selectable: false }
  38 + );
  39 +
  40 + return newNode;
  41 + };
  42 +
  43 + const getRuleChainNodeConfig = (name: string, position: XYPosition, id?: string) => {
  44 + const { getAddNodesParams } = useAddNodes();
  45 + const newNode = getAddNodesParams(
  46 + position,
  47 + {
  48 + ...new RuleChainConfig(),
  49 + data: { name },
  50 + },
  51 + {
  52 + id,
  53 + }
  54 + );
  55 +
  56 + return newNode;
  57 + };
  58 +
  59 + return { getInputNodeConfig, getOutputNodeConfig, getRuleChainNodeConfig };
24 } 60 }
@@ -27,14 +27,19 @@ import { RuleChainDetail } from '../types/ruleNode'; @@ -27,14 +27,19 @@ import { RuleChainDetail } from '../types/ruleNode';
27 import { useContextMenuAction } from './useContextMenuAction'; 27 import { useContextMenuAction } from './useContextMenuAction';
28 import { useSaveAndRedo } from './useSaveAndRedo'; 28 import { useSaveAndRedo } from './useSaveAndRedo';
29 import { EntryCategoryComponentEnum } from '../enum/category'; 29 import { EntryCategoryComponentEnum } from '../enum/category';
  30 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
  31 +import { useBasicDataTransform } from './useBasicDataTransform';
30 32
31 interface UseRuleFlowOptionsType { 33 interface UseRuleFlowOptionsType {
32 id: string; 34 id: string;
33 ruleChainDetail: Ref<RuleChainDetail | undefined>; 35 ruleChainDetail: Ref<RuleChainDetail | undefined>;
34 - createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;  
35 - createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;  
36 - updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;  
37 - updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>; 36 + modalActionType: {
  37 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  38 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  39 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  40 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  41 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  42 + };
38 useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>; 43 useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>;
39 } 44 }
40 45
@@ -47,14 +52,10 @@ const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) => @@ -47,14 +52,10 @@ const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) =>
47 }; 52 };
48 53
49 export function useRuleFlow(options: UseRuleFlowOptionsType) { 54 export function useRuleFlow(options: UseRuleFlowOptionsType) {
50 - const {  
51 - id,  
52 - ruleChainDetail,  
53 - createEdgeModalActionType,  
54 - updateEdgeDrawerActionType,  
55 - updateNodeDrawerActionType,  
56 - useSaveAndRedoActionType,  
57 - } = options; 55 + const { id, ruleChainDetail, modalActionType, useSaveAndRedoActionType } = options;
  56 +
  57 + const { createEdgeModalActionType, updateEdgeDrawerActionType, updateNodeDrawerActionType } =
  58 + modalActionType;
58 59
59 const { triggerChange } = useSaveAndRedoActionType; 60 const { triggerChange } = useSaveAndRedoActionType;
60 61
@@ -162,29 +163,35 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -162,29 +163,35 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
162 163
163 const { handleContextMenuAction } = useContextMenuAction(); 164 const { handleContextMenuAction } = useContextMenuAction();
164 165
  166 + const { validateCanCreateRuleChain } = useBasicDataTransform();
  167 +
165 onNodeContextMenu(async (params) => { 168 onNodeContextMenu(async (params) => {
166 const menuType = params.node.selected 169 const menuType = params.node.selected
167 ? await createElementsSelectedContextMenu( 170 ? await createElementsSelectedContextMenu(
168 params, 171 params,
169 unref(useSaveAndRedoActionType.changeMarker), 172 unref(useSaveAndRedoActionType.changeMarker),
170 - validateSelectionElementsCanCreateRuleChain() 173 + validateCanCreateRuleChain(
  174 + flowActionType.getSelectedNodes,
  175 + flowActionType.getSelectedEdges
  176 + ).flag
171 ) 177 )
172 : await createNodeContextMenu(params); 178 : await createNodeContextMenu(params);
173 179
174 - if (menuType) {  
175 - if (menuType === RuleContextMenuEnum.DETAIL) {  
176 - handleUpdateNode(params.node);  
177 - return;  
178 - } 180 + if (!menuType) return;
179 181
180 - handleContextMenuAction({  
181 - menuType,  
182 - flowActionType,  
183 - event: params.event,  
184 - node: params.node,  
185 - useSaveAndRedoActionType,  
186 - }); 182 + if (menuType === RuleContextMenuEnum.DETAIL) {
  183 + handleUpdateNode(params.node);
  184 + return;
187 } 185 }
  186 +
  187 + handleContextMenuAction({
  188 + menuType,
  189 + flowActionType,
  190 + event: params.event,
  191 + node: params.node,
  192 + useSaveAndRedoActionType,
  193 + modalActionType,
  194 + });
188 }); 195 });
189 196
190 onEdgeContextMenu(async (params) => { 197 onEdgeContextMenu(async (params) => {
@@ -207,6 +214,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -207,6 +214,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
207 event: params.event, 214 event: params.event,
208 useSaveAndRedoActionType, 215 useSaveAndRedoActionType,
209 edge: params.edge, 216 edge: params.edge,
  217 + modalActionType,
210 }); 218 });
211 } 219 }
212 }); 220 });
@@ -216,21 +224,25 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -216,21 +224,25 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
216 ? await createElementsSelectedContextMenu( 224 ? await createElementsSelectedContextMenu(
217 getCreatePanelContextMenuParams(params), 225 getCreatePanelContextMenuParams(params),
218 unref(useSaveAndRedoActionType.changeMarker), 226 unref(useSaveAndRedoActionType.changeMarker),
219 - validateSelectionElementsCanCreateRuleChain() 227 + validateCanCreateRuleChain(
  228 + flowActionType.getSelectedNodes,
  229 + flowActionType.getSelectedEdges
  230 + ).flag
220 ) 231 )
221 : await createPanelContextMenu( 232 : await createPanelContextMenu(
222 getCreatePanelContextMenuParams(params), 233 getCreatePanelContextMenuParams(params),
223 unref(useSaveAndRedoActionType.changeMarker) 234 unref(useSaveAndRedoActionType.changeMarker)
224 ); 235 );
225 236
226 - if (menuType) {  
227 - handleContextMenuAction({  
228 - menuType,  
229 - flowActionType,  
230 - event: params,  
231 - useSaveAndRedoActionType,  
232 - });  
233 - } 237 + if (!menuType) return;
  238 +
  239 + handleContextMenuAction({
  240 + menuType,
  241 + flowActionType,
  242 + event: params,
  243 + useSaveAndRedoActionType,
  244 + modalActionType,
  245 + });
234 }); 246 });
235 247
236 /** 248 /**
@@ -382,17 +394,5 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -382,17 +394,5 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
382 } as NodeMouseEvent; 394 } as NodeMouseEvent;
383 } 395 }
384 396
385 - function validateSelectionElementsCanCreateRuleChain() {  
386 - const nodes = unref(flowActionType.getSelectedNodes);  
387 - const edges = unref(flowActionType.getSelectedEdges);  
388 -  
389 - for (const node of nodes) {  
390 - const index = edges.findIndex((edge) => edge.target === node.id || edge.source === node.id);  
391 - if (!~index) return false;  
392 - }  
393 -  
394 - return true;  
395 - }  
396 -  
397 return { flowActionType }; 397 return { flowActionType };
398 } 398 }
1 -import type { VueFlowStore, Getters, Elements } from '@vue-flow/core';  
2 -import { ComputedRef, computed, ref, unref } from 'vue';  
3 -import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';  
4 -import { EntryCategoryComponentEnum } from '../enum/category'; 1 +import type { VueFlowStore, Elements } from '@vue-flow/core';
  2 +import { computed, ref, unref } from 'vue';
  3 +import { BasicNodeBindData, NodeData } from '../types/node';
5 import { useBasicDataTransform } from './useBasicDataTransform'; 4 import { useBasicDataTransform } from './useBasicDataTransform';
6 import { 5 import {
7 getRuleChainData, 6 getRuleChainData,
@@ -10,15 +9,13 @@ import { @@ -10,15 +9,13 @@ import {
10 saveRuleChainDetail, 9 saveRuleChainDetail,
11 } from '/@/api/ruleDesigner'; 10 } from '/@/api/ruleDesigner';
12 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode'; 11 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode';
13 -import { useInputNode } from './useInputNode'; 12 +import { useNewNode } from './useNewNode';
14 import { buildUUID } from '/@/utils/uuid'; 13 import { buildUUID } from '/@/utils/uuid';
15 import { useRoute, useRouter } from 'vue-router'; 14 import { useRoute, useRouter } from 'vue-router';
16 import { RuleChainEntityType } from '../enum/entity'; 15 import { RuleChainEntityType } from '../enum/entity';
17 import { PageEnum } from '/@/enums/pageEnum'; 16 import { PageEnum } from '/@/enums/pageEnum';
18 import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache'; 17 import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache';
19 18
20 -const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];  
21 -  
22 export function useSaveAndRedo() { 19 export function useSaveAndRedo() {
23 const changeMarker = ref(false); 20 const changeMarker = ref(false);
24 21
@@ -38,7 +35,7 @@ export function useSaveAndRedo() { @@ -38,7 +35,7 @@ export function useSaveAndRedo() {
38 35
39 const ruleChainDetail = ref<RuleChainDetail>(); 36 const ruleChainDetail = ref<RuleChainDetail>();
40 37
41 - const { mergeData, deconstructionData } = useBasicDataTransform(); 38 + const { combineData, deconstructionData } = useBasicDataTransform();
42 39
43 const triggerChange = () => { 40 const triggerChange = () => {
44 changeMarker.value = true; 41 changeMarker.value = true;
@@ -48,90 +45,14 @@ export function useSaveAndRedo() { @@ -48,90 +45,14 @@ export function useSaveAndRedo() {
48 changeMarker.value = false; 45 changeMarker.value = false;
49 }; 46 };
50 47
51 - /**  
52 - * @description 保存连接信息  
53 - */  
54 - function getConnections(  
55 - nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],  
56 - edges: ComputedRef<Getters['getEdges']>  
57 - ) {  
58 - const nodeIndexMap = new Map();  
59 -  
60 - const connections: ConnectionItemType[] = [];  
61 -  
62 - unref(nodesRef).forEach((item, index) => {  
63 - nodeIndexMap.set(item.id, index);  
64 - });  
65 -  
66 - for (const item of unref(edges)) {  
67 - const { data, target, source } = item;  
68 - const { data: bindData } = data as EdgeData;  
69 - const { type } = bindData || {};  
70 - const fromIndex = nodeIndexMap.get(source);  
71 - const toIndex = nodeIndexMap.get(target);  
72 - type?.forEach((key) => {  
73 - connections.push({ fromIndex, toIndex, type: key });  
74 - });  
75 - }  
76 -  
77 - return connections;  
78 - }  
79 -  
80 - function getNodes(nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes']) {  
81 - const nodes: BasicNodeBindData[] = [];  
82 -  
83 - for (const node of unref(nodesRef)) {  
84 - const nodeData = node.data as NodeData;  
85 -  
86 - if (ignoreNodeKeys.includes(nodeData.config?.key as string)) continue;  
87 -  
88 - const data = nodeData.data;  
89 -  
90 - nodes.push(  
91 - Object.assign(  
92 - mergeData(data, nodeData, node),  
93 - nodeData.created  
94 - ? ({  
95 - id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },  
96 - } as BasicNodeBindData)  
97 - : {}  
98 - )  
99 - );  
100 - }  
101 -  
102 - return nodes;  
103 - }  
104 -  
105 - function getFirsetNodeIndex(  
106 - nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],  
107 - edges: ComputedRef<Getters['getEdges']>  
108 - ) {  
109 - const inputNode = unref(edges).find(  
110 - (item) => (item.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT  
111 - );  
112 -  
113 - if (inputNode) {  
114 - const targetId = inputNode.target;  
115 - const index = unref(nodesRef).findIndex((item) => item.id === targetId);  
116 - return index;  
117 - }  
118 - }  
119 -  
120 const handleApplyChange = (flowActionType: VueFlowStore) => { 48 const handleApplyChange = (flowActionType: VueFlowStore) => {
121 if (!unref(changeMarker)) return; 49 if (!unref(changeMarker)) return;
122 50
123 - const edgesRef = flowActionType.getEdges;  
124 -  
125 - const extraIgnoreNodeRef = unref(flowActionType.getNodes).filter(  
126 - (item) => !ignoreNodeKeys.includes((item.data as NodeData).config?.key as string) 51 + const { connections, nodes, firstNodeIndex } = combineData(
  52 + flowActionType.getNodes,
  53 + flowActionType.getEdges
127 ); 54 );
128 55
129 - const connections = getConnections(extraIgnoreNodeRef, edgesRef);  
130 -  
131 - const nodes = getNodes(extraIgnoreNodeRef);  
132 -  
133 - const firstNodeIndex = getFirsetNodeIndex(extraIgnoreNodeRef, edgesRef);  
134 -  
135 handleSaveRuleChain(connections, nodes, firstNodeIndex); 56 handleSaveRuleChain(connections, nodes, firstNodeIndex);
136 }; 57 };
137 58
@@ -211,7 +132,7 @@ export function useSaveAndRedo() { @@ -211,7 +132,7 @@ export function useSaveAndRedo() {
211 function parseRuleChain(ruleChain: RuleChainType) { 132 function parseRuleChain(ruleChain: RuleChainType) {
212 const inputId = buildUUID(); 133 const inputId = buildUUID();
213 134
214 - const { getInputNodeConfig } = useInputNode(); 135 + const { getInputNodeConfig } = useNewNode();
215 136
216 const value = deconstructionData(ruleChain, inputId); 137 const value = deconstructionData(ruleChain, inputId);
217 138
@@ -19,10 +19,11 @@ @@ -19,10 +19,11 @@
19 import { CreateEdgeModal } from './src/components/CreateEdgeModal'; 19 import { CreateEdgeModal } from './src/components/CreateEdgeModal';
20 import { useFullScreen } from './hook/useFullScreen'; 20 import { useFullScreen } from './hook/useFullScreen';
21 import { useSaveAndRedo } from './hook/useSaveAndRedo'; 21 import { useSaveAndRedo } from './hook/useSaveAndRedo';
  22 + import { NodeData } from './types/node';
22 import { Icon } from '/@/components/Icon'; 23 import { Icon } from '/@/components/Icon';
23 import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer'; 24 import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer';
24 import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer'; 25 import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer';
25 - import { NodeData } from './types/node'; 26 + import { CreateRuleChainModal } from './src/components/CreateRuleChainModal';
26 27
27 const getId = Number(Math.random().toString().substring(2)).toString(16); 28 const getId = Number(Math.random().toString().substring(2)).toString(16);
28 29
@@ -36,6 +37,9 @@ @@ -36,6 +37,9 @@
36 37
37 const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null); 38 const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null);
38 39
  40 + const createRuleChainModalActionType =
  41 + ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>(null);
  42 +
39 const flowElRef = ref<Nullable<FlowElRef>>(null); 43 const flowElRef = ref<Nullable<FlowElRef>>(null);
40 44
41 const elements = ref([]); 45 const elements = ref([]);
@@ -57,11 +61,14 @@ @@ -57,11 +61,14 @@
57 const { flowActionType } = useRuleFlow({ 61 const { flowActionType } = useRuleFlow({
58 id: getId, 62 id: getId,
59 ruleChainDetail, 63 ruleChainDetail,
60 - createNodeModalActionType,  
61 - createEdgeModalActionType,  
62 - updateEdgeDrawerActionType,  
63 - updateNodeDrawerActionType,  
64 useSaveAndRedoActionType, 64 useSaveAndRedoActionType,
  65 + modalActionType: {
  66 + createNodeModalActionType,
  67 + createEdgeModalActionType,
  68 + updateEdgeDrawerActionType,
  69 + updateNodeDrawerActionType,
  70 + createRuleChainModalActionType,
  71 + },
65 }); 72 });
66 73
67 const { handleOnDragOver, handleOnDrop } = useDragCreate({ 74 const { handleOnDragOver, handleOnDrop } = useDragCreate({
@@ -85,6 +92,8 @@ @@ -85,6 +92,8 @@
85 92
86 const handleDeleteSelectionElements = () => { 93 const handleDeleteSelectionElements = () => {
87 flowActionType.removeNodes(unref(flowActionType.getSelectedNodes)); 94 flowActionType.removeNodes(unref(flowActionType.getSelectedNodes));
  95 + flowActionType.removeEdges(unref(flowActionType.getSelectedEdges));
  96 + useSaveAndRedoActionType.triggerChange?.();
88 }; 97 };
89 98
90 onMounted(() => { 99 onMounted(() => {
@@ -97,6 +106,7 @@ @@ -97,6 +106,7 @@
97 createNodeModalActionType, 106 createNodeModalActionType,
98 updateEdgeDrawerActionType, 107 updateEdgeDrawerActionType,
99 updateNodeDrawerActionType, 108 updateNodeDrawerActionType,
  109 + createRuleChainModalActionType,
100 flowActionType, 110 flowActionType,
101 triggerChange, 111 triggerChange,
102 }); 112 });
@@ -177,6 +187,8 @@ @@ -177,6 +187,8 @@
177 187
178 <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" /> 188 <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" />
179 <UpdateNodeDrawer ref="updateNodeDrawerActionType" /> 189 <UpdateNodeDrawer ref="updateNodeDrawerActionType" />
  190 +
  191 + <CreateRuleChainModal ref="createRuleChainModalActionType" />
180 </main> 192 </main>
181 </template> 193 </template>
182 194
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +export enum FormFieldsEnum {
  4 + NAME = 'name',
  5 + DESCRIPTION = 'description',
  6 + ADDITIONAL_INFO = 'additionalInfo',
  7 +}
  8 +
  9 +export enum FormFieldsNameEnum {
  10 + NAME = '名称',
  11 + DESCRIPTION = '说明',
  12 +}
  13 +
  14 +export const formSchemas: FormSchema[] = [
  15 + {
  16 + field: FormFieldsEnum.NAME,
  17 + label: FormFieldsNameEnum.NAME,
  18 + component: 'Input',
  19 + required: true,
  20 + componentProps: {
  21 + placeholder: `请输入${FormFieldsNameEnum.NAME}`,
  22 + },
  23 + },
  24 + {
  25 + field: FormFieldsEnum.DESCRIPTION,
  26 + label: FormFieldsNameEnum.DESCRIPTION,
  27 + component: 'InputTextArea',
  28 + componentProps: {
  29 + placeholder: `请输入${FormFieldsNameEnum.DESCRIPTION}`,
  30 + },
  31 + },
  32 +];
  1 +export { default as CreateRuleChainModal } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { BasicModal, useModalInner } from '/@/components/Modal';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchemas, FormFieldsEnum } from './config';
  5 + import { ref } from 'vue';
  6 +
  7 + interface SaveParamsType {
  8 + name: string;
  9 + additional: { description: string };
  10 + }
  11 +
  12 + const visible = ref(false);
  13 +
  14 + const [register, { closeModal }] = useModalInner();
  15 +
  16 + const [registerForm, { getFieldsValue, validate }] = useForm({
  17 + schemas: formSchemas,
  18 + showActionButtonGroup: false,
  19 + layout: 'vertical',
  20 + });
  21 +
  22 + let resolveFn: undefined | ((value: SaveParamsType) => any);
  23 +
  24 + const openCreateRuleChainModal = async (): Promise<{
  25 + name: string;
  26 + additionalInfo: { description: string };
  27 + }> => {
  28 + visible.value = true;
  29 + return new Promise((resolve) => {
  30 + resolveFn = resolve;
  31 + });
  32 + };
  33 +
  34 + const handleSave = async () => {
  35 + await validate();
  36 + const value = getFieldsValue();
  37 + resolveFn?.({
  38 + name: value[FormFieldsEnum.NAME],
  39 + additional: { description: value[FormFieldsEnum.DESCRIPTION] || '' },
  40 + });
  41 + closeModal();
  42 + };
  43 +
  44 + defineExpose({ openCreateRuleChainModal });
  45 +</script>
  46 +
  47 +<template>
  48 + <BasicModal
  49 + v-model:visible="visible"
  50 + @register="register"
  51 + @ok="handleSave"
  52 + title="Create nested rule chain"
  53 + >
  54 + <BasicForm @register="registerForm" />
  55 + </BasicModal>
  56 +</template>
@@ -24,7 +24,7 @@ export interface RuleChainDetail { @@ -24,7 +24,7 @@ export interface RuleChainDetail {
24 createdTime: number; 24 createdTime: number;
25 additionalInfo: AdditionalInfo; 25 additionalInfo: AdditionalInfo;
26 tenantId: Id; 26 tenantId: Id;
27 - name: Id; 27 + name: string;
28 type: string; 28 type: string;
29 firstRuleNodeId: Id; 29 firstRuleNodeId: Id;
30 root: boolean; 30 root: boolean;