Showing
9 changed files
with
119 additions
and
15 deletions
... | ... | @@ -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, | ... | ... |
... | ... | @@ -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, | ... | ... |