Commit 02fc83cf02f48874629e60ca9f82ec891cdb58ca

Authored by ww
1 parent f00aade9

feat: 实现规则链导入逻辑

... ... @@ -4,12 +4,23 @@ import { defHttp } from '/@/utils/http/axios';
4 4 import { RuleChainDetail, RuleChainType } from '/@/views/rule/designer/types/ruleNode';
5 5
6 6 enum Api {
  7 + SAVE_RULE_CHAINS = '/ruleChain',
7 8 GET_RULE_CHAINS_DETAIL = '/ruleChain',
8 9 SAVE = '/ruleChain/metadata',
9 10 GET_RULE_CHAINES = '/ruleChains',
10 11 GET_RULE_NODE_EVENTS = '/events/RULE_NODE',
11 12 }
12 13
  14 +export const saveRuleChainDetail = (params: Partial<RuleChainDetail>) => {
  15 + return defHttp.post<RuleChainDetail>(
  16 + {
  17 + url: Api.SAVE_RULE_CHAINS,
  18 + data: params,
  19 + },
  20 + { joinPrefix: false }
  21 + );
  22 +};
  23 +
13 24 export const getRuleChainDetail = (id: string) => {
14 25 return defHttp.get<RuleChainDetail>(
15 26 {
... ...
... ... @@ -41,8 +41,12 @@ export const MENU_LIST = 'MENU_LIST';
41 41
42 42 export const RULE_NODE_LOCAL_CACHE_KEY = 'RULE__NODE__KEY__';
43 43
  44 +export const RULE_CHAIN_IMPORT_LOCAL_CACHE_KEY = 'RULE__CHAIN__IMPORT__KEY__';
  45 +
44 46 export const RULE_NODE_KEY = 'RULE_NODE';
45 47
  48 +export const RULE_CHAIN_KEY = 'RULE_CHAIN';
  49 +
46 50 export enum CacheTypeEnum {
47 51 SESSION,
48 52 LOCAL,
... ...
... ... @@ -18,4 +18,6 @@ export const PageEnum = {
18 18 SHARE_PAGE: '/share/:viewType/:id/:publicId',
19 19
20 20 RULE_CHAIN_DETAIL: '/rule/chain/:id',
  21 +
  22 + RULE_CHAIN_DETAIL_IMPORT: '/rule/chain/import',
21 23 };
... ...
... ... @@ -8,6 +8,7 @@ import { buildUUID } from '/@/utils/uuid';
8 8 import { isNullOrUnDef } from '/@/utils/is';
9 9 import { useAddNodes } from './useAddNodes';
10 10 import { useAddEdges } from './useAddEdges';
  11 +import { RuleChainEntityType } from '../enum/entity';
11 12
12 13 export function useBasicDataTransform() {
13 14 const nodeConfigMap = new Map<string, NodeData>();
... ... @@ -67,8 +68,8 @@ export function useBasicDataTransform() {
67 68
68 69 const sourceNode = indexMap.get(Number(fromIndex));
69 70 const targetNode = indexMap.get(Number(toIndex));
70   - const source = sourceNode!.id!.id;
71   - const target = targetNode!.id!.id;
  71 + const source = sourceNode?.id?.id || buildUUID();
  72 + const target = targetNode?.id?.id || buildUUID();
72 73 const sourceHandle = `${source}${SOURCE_HANDLE}`;
73 74 const targetHandle = `${target}${TARGET_HANDLE}`;
74 75
... ... @@ -88,7 +89,7 @@ export function useBasicDataTransform() {
88 89 if (!isNullOrUnDef(firstNodeIndex)) {
89 90 const targetNode = indexMap.get(firstNodeIndex);
90 91 const source = inputNodeId;
91   - const target = targetNode!.id!.id;
  92 + const target = targetNode?.id?.id || buildUUID();
92 93 const sourceHandle = `${source}$${SOURCE_HANDLE}`;
93 94 const targetHandle = `${target}${TARGET_HANDLE}`;
94 95 edges.push(
... ... @@ -112,7 +113,7 @@ export function useBasicDataTransform() {
112 113 const { layoutX, layoutY, description } = additionalInfo || {};
113 114 const { getAddNodesParams } = useAddNodes();
114 115
115   - return getAddNodesParams(
  116 + const value = getAddNodesParams(
116 117 { x: layoutX!, y: layoutY! },
117 118 {
118 119 ...config,
... ... @@ -128,6 +129,10 @@ export function useBasicDataTransform() {
128 129 id: id?.id || buildUUID(),
129 130 }
130 131 );
  132 +
  133 + if (!id?.id) node.id = { id: value.id, entityType: RuleChainEntityType.RULE_NODE };
  134 +
  135 + return value;
131 136 }
132 137
133 138 function deconstructionNode(nodes: RuleChainType['nodes']) {
... ...
1 1 import { GraphEdge, GraphNode, VueFlowStore, pointToRendererPoint } from '@vue-flow/core';
2   -import { getRuleNodeCache, setRuleNodeCache } from './useRuleCopyPaste';
  2 +import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache';
3 3 import { RuleContextMenuEnum } from './useRuleChainContextMenu';
4 4 import { useAddNodes } from './useAddNodes';
5 5 import { buildUUID } from '/@/utils/uuid';
... ...
src/views/rule/designer/hook/useRuleChainCache.ts renamed from src/views/rule/designer/hook/useRuleCopyPaste.ts
1 1 import { Edge, Node } from '@vue-flow/core';
2   -import { RULE_NODE_KEY, RULE_NODE_LOCAL_CACHE_KEY } from '/@/enums/cacheEnum';
  2 +import {
  3 + RULE_NODE_KEY,
  4 + RULE_CHAIN_KEY,
  5 + RULE_NODE_LOCAL_CACHE_KEY,
  6 + RULE_CHAIN_IMPORT_LOCAL_CACHE_KEY,
  7 +} from '/@/enums/cacheEnum';
3 8 import { createLocalStorage } from '/@/utils/cache';
  9 +import { RuleChainDetail, RuleChainType } from '../types/ruleNode';
4 10
5 11 const ruleNodeStorage = createLocalStorage({ prefixKey: RULE_NODE_LOCAL_CACHE_KEY });
  12 +const ruleChainStorage = createLocalStorage({ prefixKey: RULE_CHAIN_IMPORT_LOCAL_CACHE_KEY });
6 13
7 14 interface RuleNodeCacheType {
8 15 nodes?: Node[];
... ... @@ -11,6 +18,11 @@ interface RuleNodeCacheType {
11 18 originY?: number;
12 19 }
13 20
  21 +export interface RuleChainCacheType {
  22 + ruleChain: Partial<RuleChainDetail>;
  23 + metadata: RuleChainType;
  24 +}
  25 +
14 26 export const setRuleNodeCache = ({
15 27 nodes = [],
16 28 edges = [],
... ... @@ -29,6 +41,14 @@ export const getRuleNodeCache = (): RuleNodeCacheType => ruleNodeStorage.get(RUL
29 41
30 42 export const checkHasCacheRuleNode = () => !!getRuleNodeCache();
31 43
  44 +export const getRuleChainImportCache = (): RuleChainCacheType =>
  45 + ruleChainStorage.get(RULE_CHAIN_KEY) || {};
  46 +
  47 +export const setRuleChainImportCache = (value: RuleChainCacheType) =>
  48 + ruleChainStorage.set(RULE_CHAIN_KEY, value);
  49 +
  50 +export const clearRuleChainImportCache = () => ruleChainStorage.remove(RULE_CHAIN_KEY);
  51 +
32 52 function initRuleNodeStorage() {
33 53 const value = ruleNodeStorage.get(RULE_NODE_KEY);
34 54 value && ruleNodeStorage.set(RULE_NODE_KEY, value);
... ...
... ... @@ -2,7 +2,7 @@ import { NodeMouseEvent } from '@vue-flow/core';
2 2 import { useContextMenu } from '../src/components/RuleChainContextMenu';
3 3 import { RuleChainContextMenuItemType } from '../src/components/RuleChainContextMenu/index.type';
4 4 import { ElementsTypeEnum } from '../enum';
5   -import { checkHasCacheRuleNode } from './useRuleCopyPaste';
  5 +import { checkHasCacheRuleNode } from './useRuleChainCache';
6 6
7 7 export enum RuleContextMenuEnum {
8 8 DETAIL = 'DETAIL',
... ... @@ -17,6 +17,8 @@ export enum RuleContextMenuEnum {
17 17 UNDO_CHANGE = 'UNDO_CHANGE',
18 18
19 19 SELECT_ALL = 'SELECT_ALL',
  20 +
  21 + CREATE_RULE_CHAIN = 'CREATE_RULE_CHAIN',
20 22 }
21 23
22 24 export enum RuleContextMenuNameEnum {
... ... @@ -32,6 +34,8 @@ export enum RuleContextMenuNameEnum {
32 34 UNDO_CHANGE = '撤销更改',
33 35
34 36 SELECT_ALL = '选择全部',
  37 +
  38 + CREATE_RULE_CHAIN = '创建规则链',
35 39 }
36 40
37 41 export enum RuleChainContextMenuIconEnum {
... ... @@ -48,6 +52,8 @@ export enum RuleChainContextMenuIconEnum {
48 52
49 53 SELECT_ALL = 'material-symbols:select-all',
50 54
  55 + CREATE_RULE_CHAIN = 'material-symbols:settings-ethernet',
  56 +
51 57 // LINK = 'material-symbols:trending-flat',
52 58 }
53 59
... ... @@ -95,6 +101,7 @@ export function useCreateRuleChainContextMenu() {
95 101 const createElementsSelectedContextMenu = (
96 102 params: NodeMouseEvent,
97 103 changeMarker: boolean,
  104 + hasCreateChainMenuItem = false,
98 105 elementsType: ElementsTypeEnum.NODE = ElementsTypeEnum.NODE
99 106 ): Promise<RuleContextMenuEnum | ''> => {
100 107 return new Promise(async (resolve) => {
... ... @@ -106,6 +113,9 @@ export function useCreateRuleChainContextMenu() {
106 113 getMenuItem(RuleContextMenuEnum.PASTE, resolve, !checkHasCacheRuleNode()),
107 114 getDivider(),
108 115 getMenuItem(RuleContextMenuEnum.UNSELECTED, resolve),
  116 + ...(hasCreateChainMenuItem
  117 + ? [getMenuItem(RuleContextMenuEnum.CREATE_RULE_CHAIN, resolve)]
  118 + : []),
109 119 getMenuItem(RuleContextMenuEnum.DELETE_SELECT, resolve),
110 120 getDivider(),
111 121 getMenuItem(RuleContextMenuEnum.APPLY_CHANGE, resolve, !changeMarker),
... ...
... ... @@ -166,7 +166,8 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
166 166 const menuType = params.node.selected
167 167 ? await createElementsSelectedContextMenu(
168 168 params,
169   - unref(useSaveAndRedoActionType.changeMarker)
  169 + unref(useSaveAndRedoActionType.changeMarker),
  170 + validateSelectionElementsCanCreateRuleChain()
170 171 )
171 172 : await createNodeContextMenu(params);
172 173
... ... @@ -214,7 +215,8 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
214 215 const menuType = unref(flowActionType.getSelectedElements).length
215 216 ? await createElementsSelectedContextMenu(
216 217 getCreatePanelContextMenuParams(params),
217   - unref(useSaveAndRedoActionType.changeMarker)
  218 + unref(useSaveAndRedoActionType.changeMarker),
  219 + validateSelectionElementsCanCreateRuleChain()
218 220 )
219 221 : await createPanelContextMenu(
220 222 getCreatePanelContextMenuParams(params),
... ... @@ -380,5 +382,17 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
380 382 } as NodeMouseEvent;
381 383 }
382 384
  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 +
383 397 return { flowActionType };
384 398 }
... ...
... ... @@ -3,12 +3,19 @@ import { ComputedRef, computed, ref, unref } from 'vue';
3 3 import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';
4 4 import { EntryCategoryComponentEnum } from '../enum/category';
5 5 import { useBasicDataTransform } from './useBasicDataTransform';
6   -import { getRuleChainData, getRuleChainDetail, saveRuleChainData } from '/@/api/ruleDesigner';
  6 +import {
  7 + getRuleChainData,
  8 + getRuleChainDetail,
  9 + saveRuleChainData,
  10 + saveRuleChainDetail,
  11 +} from '/@/api/ruleDesigner';
7 12 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode';
8 13 import { useInputNode } from './useInputNode';
9 14 import { buildUUID } from '/@/utils/uuid';
10   -import { useRoute } from 'vue-router';
  15 +import { useRoute, useRouter } from 'vue-router';
11 16 import { RuleChainEntityType } from '../enum/entity';
  17 +import { PageEnum } from '/@/enums/pageEnum';
  18 +import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache';
12 19
13 20 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
14 21
... ... @@ -21,10 +28,14 @@ export function useSaveAndRedo() {
21 28
22 29 const ROUTE = useRoute();
23 30
  31 + const ROUTER = useRouter();
  32 +
24 33 const debugMarker = ref(false);
25 34
26 35 const getRuleChainId = computed(() => (ROUTE.params as Record<'id', string>).id);
27 36
  37 + const getIsImportFlag = computed(() => ROUTE.fullPath === PageEnum.RULE_CHAIN_DETAIL_IMPORT);
  38 +
28 39 const ruleChainDetail = ref<RuleChainDetail>();
29 40
30 41 const { mergeData, deconstructionData } = useBasicDataTransform();
... ... @@ -131,7 +142,9 @@ export function useSaveAndRedo() {
131 142 };
132 143
133 144 async function getCurrentRuleChainDetail() {
134   - ruleChainDetail.value = await getRuleChainDetail(unref(getRuleChainId));
  145 + ruleChainDetail.value = unref(getIsImportFlag)
  146 + ? (getImportChainDetail() as RuleChainDetail)
  147 + : await getRuleChainDetail(unref(getRuleChainId));
135 148 }
136 149
137 150 async function handleSaveRuleChain(
... ... @@ -142,19 +155,35 @@ export function useSaveAndRedo() {
142 155 try {
143 156 loading.value = true;
144 157
  158 + let ruleChainId = unref(getRuleChainId);
  159 + if (unref(getIsImportFlag)) {
  160 + const detail = await saveRuleChainDetail(unref(ruleChainDetail)!);
  161 + ruleChainDetail.value = detail;
  162 + ruleChainId = detail.id.id;
  163 + }
  164 +
145 165 const data = await saveRuleChainData({
146 166 connections,
147 167 nodes,
148 168 firstNodeIndex,
149 169 ruleChainId: {
150 170 entityType: RuleChainEntityType.RULE_CHAIN,
151   - id: unref(getRuleChainId),
  171 + id: ruleChainId,
152 172 },
153 173 });
154 174
155 175 parseRuleChain(data);
156 176
157 177 resetChange();
  178 +
  179 + if (unref(getIsImportFlag)) {
  180 + clearRuleChainImportCache();
  181 +
  182 + ROUTER.replace({
  183 + path: `/rule/chain/${ruleChainId}`,
  184 + replace: true,
  185 + });
  186 + }
158 187 } finally {
159 188 loading.value = false;
160 189 }
... ... @@ -164,13 +193,16 @@ export function useSaveAndRedo() {
164 193 try {
165 194 loading.value = true;
166 195
167   - const data = await getRuleChainData(unref(getRuleChainId));
  196 + const data = unref(getIsImportFlag)
  197 + ? await getImportMetadata()
  198 + : await getRuleChainData(unref(getRuleChainId));
  199 + if (!data) return;
168 200
169 201 const elements = parseRuleChain(data);
170 202
171 203 flowActionType.setElements(elements);
172 204
173   - resetChange();
  205 + unref(getIsImportFlag) ? triggerChange() : resetChange();
174 206 } finally {
175 207 loading.value = false;
176 208 }
... ... @@ -201,6 +233,12 @@ export function useSaveAndRedo() {
201 233 triggerChange();
202 234 };
203 235
  236 + const getImportMetadata = () => {
  237 + return getRuleChainImportCache().metadata;
  238 + };
  239 +
  240 + const getImportChainDetail = () => getRuleChainImportCache().ruleChain;
  241 +
204 242 return {
205 243 loading,
206 244 debugMarker,
... ...