Commit 08c5e0f26cda5a512759434628397192e20beb70

Authored by ww
1 parent 6fdcb5ca

feat: 实现选中部分节点创建规则链

... ... @@ -13,6 +13,9 @@ import { EntryCategoryComponentEnum } from '../enum/category';
13 13
14 14 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
15 15
  16 +export const SOURCE_HANDLE = '__handle-right';
  17 +export const TARGET_HANDLE = '__handle-left';
  18 +
16 19 export function useBasicDataTransform() {
17 20 const nodeConfigMap = new Map<string, NodeData>();
18 21
... ... @@ -21,7 +24,7 @@ export function useBasicDataTransform() {
21 24 const category = allComponents[key as RuleNodeTypeEnum];
22 25 for (const nodeConfig of category.components) {
23 26 const { clazz } = nodeConfig;
24   - nodeConfigMap.set(clazz, { config: nodeConfig, category: category.category });
  27 + nodeConfigMap.set(clazz, { config: nodeConfig, categoryConfig: category.category });
25 28 }
26 29 }
27 30 }
... ... @@ -49,8 +52,7 @@ export function useBasicDataTransform() {
49 52 const { connections, nodes, firstNodeIndex } = unref(ruleChain);
50 53 const indexMap = new Map<number, BasicNodeBindData>();
51 54 const groupByConnections = new Map<string, string[]>();
52   - const SOURCE_HANDLE = '__handle-right';
53   - const TARGET_HANDLE = '__handle-left';
  55 +
54 56 const SEPARATOR = ',';
55 57 const edges: Elements = [];
56 58 const { getAddedgesParams } = useAddEdges();
... ... @@ -207,6 +209,8 @@ export function useBasicDataTransform() {
207 209 function getNodes(nodesRef: Ref<GraphNode[]> | GraphNode[], removeId: boolean) {
208 210 const nodes: BasicNodeBindData[] = [];
209 211
  212 + let offsetX = 0;
  213 + let offsetY = 0;
210 214 for (const node of unref(nodesRef)) {
211 215 const nodeData = node.data as NodeData;
212 216
... ... @@ -214,16 +218,31 @@ export function useBasicDataTransform() {
214 218
215 219 const data = nodeData.data;
216 220
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   - )
  221 + const resultNode = Object.assign(
  222 + mergeData(data, nodeData, node),
  223 + nodeData.created && !removeId
  224 + ? ({
  225 + id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },
  226 + } as BasicNodeBindData)
  227 + : {}
226 228 );
  229 +
  230 + if (resultNode.additionalInfo.layoutX < offsetX) offsetX = resultNode.additionalInfo.layoutX;
  231 + if (resultNode.additionalInfo.layoutY < offsetY) offsetY = resultNode.additionalInfo.layoutY;
  232 +
  233 + nodes.push(resultNode);
  234 + }
  235 +
  236 + /**
  237 + * compatible thingsboard rule chain designer.
  238 + * thingsboard rule chain designer does not have negative coordinated.
  239 + */
  240 + if (offsetX < 0 || offsetY < 0) {
  241 + nodes.forEach((node) => {
  242 + const { layoutX = 0, layoutY = 0 } = node.additionalInfo || {};
  243 + node.additionalInfo!.layoutX = layoutX + Math.abs(offsetX);
  244 + node.additionalInfo!.layoutY = layoutY + Math.abs(offsetY);
  245 + });
227 246 }
228 247
229 248 return nodes;
... ... @@ -266,9 +285,22 @@ export function useBasicDataTransform() {
266 285 nodes: Ref<GraphNode[]> | GraphNode[] = [],
267 286 edges: Ref<GraphEdge[]> | GraphEdge[] = []
268 287 ) {
  288 + let flag = true;
  289 +
  290 + if (unref(nodes).length <= 1) flag = false;
  291 +
269 292 const rootNode: GraphNode[] = [];
270 293
271   - let flag = true;
  294 + for (const edge of unref(edges)) {
  295 + if (!unref(nodes).find((node) => node.id === edge.source)) rootNode.push(edge.targetNode);
  296 + }
  297 +
  298 + for (const node of unref(nodes)) {
  299 + if (!unref(edges).some((edge) => edge.target === node.id)) rootNode.push(node);
  300 + }
  301 +
  302 + if (rootNode.length > 1) return { flag: false, firstNode: null };
  303 +
272 304 for (const node of unref(nodes)) {
273 305 const list = unref(edges).filter(
274 306 (edge) => edge.source === node.id || edge.target === node.id
... ... @@ -278,14 +310,6 @@ export function useBasicDataTransform() {
278 310 flag = false;
279 311 break;
280 312 }
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 313 }
290 314
291 315 return { flag, firstNode: rootNode[0] || null };
... ...
... ... @@ -5,7 +5,7 @@ import { useAddNodes } from './useAddNodes';
5 5 import { buildUUID } from '/@/utils/uuid';
6 6 import { Ref, toRaw, unref } from 'vue';
7 7 import { useSaveAndRedo } from './useSaveAndRedo';
8   -import { isUnDef } from '/@/utils/is';
  8 +import { isNullOrUnDef, isUnDef } from '/@/utils/is';
9 9 import { useAddEdges } from './useAddEdges';
10 10 import { EdgeData } from '../types/node';
11 11 import { CreateNodeModal } from '../src/components/CreateNodeModal';
... ... @@ -13,9 +13,11 @@ import { CreateEdgeModal } from '../src/components/CreateEdgeModal';
13 13 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
14 14 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
15 15 import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
16   -import { useBasicDataTransform } from './useBasicDataTransform';
  16 +import { SOURCE_HANDLE, TARGET_HANDLE, useBasicDataTransform } from './useBasicDataTransform';
17 17 import { cloneDeep } from 'lodash-es';
18 18 import { useNewNode } from './useNewNode';
  19 +import { RuleChainDetail } from '../types/ruleNode';
  20 +import { saveRuleChainData, saveRuleChainDetail } from '/@/api/ruleDesigner';
19 21
20 22 interface HandleContextMenuActionParamsType {
21 23 menuType: RuleContextMenuEnum;
... ... @@ -47,11 +49,15 @@ export function transformToRuleChain(
47 49 const outputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.target));
48 50 const outputEdgesId = outputEdges.map((edge) => edge.id);
49 51
  52 + const inputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.source));
  53 + const inputEdgesId = inputEdges.map((edge) => edge.id);
  54 +
50 55 const { getOutputNodeConfig } = useNewNode();
51 56 const outputNode = outputEdges.map((edge) => {
52 57 const id = buildUUID();
53 58 const name = (edge.data as EdgeData).data?.type?.join(' / ') || '';
54 59 edge.target = id;
  60 + edge.targetHandle = `${id}${TARGET_HANDLE}`;
55 61 return getOutputNodeConfig(name, edge.targetNode.position, id);
56 62 });
57 63
... ... @@ -61,9 +67,9 @@ export function transformToRuleChain(
61 67
62 68 const { firstNode } = validateCanCreateRuleChain(nodesRef, edgesRef);
63 69
64   - const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode.id);
  70 + const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode?.id);
65 71
66   - return { connections, nodes, firstNodeIndex, outputEdgesId };
  72 + return { connections, nodes, firstNodeIndex, outputEdgesId, inputEdgesId };
67 73 }
68 74
69 75 export const NODE_WIDTH = 176;
... ... @@ -87,20 +93,13 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
87 93 continue;
88 94 }
89 95
90   - if (x < leftTopX!) {
91   - leftTopX = x;
92   - if (y < leftTopY!) {
93   - leftTopY = y;
94   - }
95   - continue;
96   - }
  96 + if (x < leftTopX!) leftTopX = x;
97 97
98   - if (x + NODE_WIDTH > rightBottomX!) {
99   - rightBottomX = x + NODE_WIDTH;
100   - if (y + NODE_HEIGHT > rightBottomY!) {
101   - rightBottomY = y + NODE_HEIGHT;
102   - }
103   - }
  98 + if (y < leftTopY!) leftTopY = y;
  99 +
  100 + if (x + NODE_WIDTH > rightBottomX!) rightBottomX = x + NODE_WIDTH;
  101 +
  102 + if (y + NODE_HEIGHT > rightBottomY!) rightBottomY = y + NODE_HEIGHT;
104 103 }
105 104
106 105 return {
... ... @@ -109,6 +108,60 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
109 108 };
110 109 }
111 110
  111 +function createRuleChainNode({
  112 + originX,
  113 + originY,
  114 + outputEdges,
  115 + inputEdges,
  116 + ruleChainDetail,
  117 +}: {
  118 + originX: number;
  119 + originY: number;
  120 + ruleChainDetail: RuleChainDetail;
  121 + outputEdges: Ref<(GraphEdge | undefined)[]> | (GraphEdge | undefined)[];
  122 + inputEdges: Ref<(GraphEdge | undefined)[]> | (GraphEdge | undefined)[];
  123 +}) {
  124 + const { getRuleChainNodeConfig } = useNewNode();
  125 + const { getAddedgesParams } = useAddEdges();
  126 + const nodeId = buildUUID();
  127 +
  128 + const ruleChainNode = getRuleChainNodeConfig({
  129 + name: ruleChainDetail.name,
  130 + position: { x: originX - NODE_WIDTH / 2, y: originY - NODE_HEIGHT / 2 },
  131 + ruleChainId: ruleChainDetail.id.id,
  132 + id: nodeId,
  133 + });
  134 +
  135 + const newInputEdges = unref(inputEdges).map((edge) =>
  136 + getAddedgesParams(
  137 + {
  138 + source: edge!.source,
  139 + target: nodeId,
  140 + sourceHandle: `${edge?.source}${SOURCE_HANDLE}`,
  141 + targetHandle: `${nodeId}${TARGET_HANDLE}`,
  142 + },
  143 + toRaw(unref(edge!.data as EdgeData).data)
  144 + )
  145 + );
  146 +
  147 + const newOutputEdges = unref(outputEdges).map((edge) =>
  148 + getAddedgesParams(
  149 + {
  150 + source: nodeId,
  151 + target: edge!.target,
  152 + sourceHandle: `${nodeId}${SOURCE_HANDLE}`,
  153 + targetHandle: `${edge?.target}${TARGET_HANDLE}`,
  154 + },
  155 + toRaw(unref(edge!.data as EdgeData).data)
  156 + )
  157 + );
  158 +
  159 + return {
  160 + nodes: [ruleChainNode],
  161 + edges: [...newInputEdges, ...newOutputEdges],
  162 + };
  163 +}
  164 +
112 165 export function useContextMenuAction() {
113 166 const copy = (params: HandleContextMenuActionParamsType) => {
114 167 const { node } = params;
... ... @@ -248,35 +301,51 @@ export function useContextMenuAction() {
248 301 useSaveAndRedoActionType?.handleRedoChange(flowActionType!);
249 302 };
250 303
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();
  304 + const createRuleChain = async (params: HandleContextMenuActionParamsType) => {
  305 + const { useSaveAndRedoActionType, modalActionType, flowActionType } = params;
  306 + const { createRuleChainModalActionType } = modalActionType;
  307 + const result = (await unref(createRuleChainModalActionType)?.openCreateRuleChainModal()) as {
  308 + name: string;
  309 + additionalInfo: { description: string };
  310 + };
  311 +
  312 + const ruleChainDetail = await saveRuleChainDetail(
  313 + Object.assign(result, { debugger: false, type: 'CORE' }) as Partial<RuleChainDetail>
  314 + );
  315 +
  316 + const selectedNodes = unref(flowActionType?.getSelectedNodes);
  317 + const selectedEdges = unref(flowActionType?.getSelectedEdges);
  318 + const { firstNodeIndex, connections, nodes, outputEdgesId, inputEdgesId } =
  319 + transformToRuleChain(selectedNodes, selectedEdges);
  320 +
  321 + await saveRuleChainData({
  322 + firstNodeIndex,
  323 + connections: connections.filter(
  324 + (connection) => !isNullOrUnDef(connection.fromIndex) && !isNullOrUnDef(connection.toIndex)
  325 + ),
  326 + nodes,
  327 + ruleChainId: ruleChainDetail.id,
  328 + });
  329 +
  330 + const outputEdges = outputEdgesId.map((id) => flowActionType?.findEdge(id));
  331 + const inputEdges = inputEdgesId.map((id) => flowActionType?.findEdge(id));
  332 +
  333 + const { originX, originY } = getElementsCenter(unref(selectedNodes));
  334 +
  335 + flowActionType?.removeNodes(selectedNodes || []);
  336 + flowActionType?.removeEdges(selectedEdges || []);
  337 +
  338 + const { nodes: newNode, edges: newEdges } = createRuleChainNode({
  339 + originX,
  340 + originY,
  341 + outputEdges,
  342 + inputEdges,
  343 + ruleChainDetail,
  344 + });
  345 +
  346 + flowActionType?.addNodes(newNode);
  347 + flowActionType?.addEdges(newEdges);
  348 + useSaveAndRedoActionType?.triggerChange();
280 349 };
281 350
282 351 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
... ...
... ... @@ -40,13 +40,23 @@ export function useNewNode() {
40 40 return newNode;
41 41 };
42 42
43   - const getRuleChainNodeConfig = (name: string, position: XYPosition, id?: string) => {
  43 + const getRuleChainNodeConfig = ({
  44 + name,
  45 + position,
  46 + ruleChainId,
  47 + id,
  48 + }: {
  49 + name: string;
  50 + position: XYPosition;
  51 + ruleChainId: string;
  52 + id?: string;
  53 + }) => {
44 54 const { getAddNodesParams } = useAddNodes();
45 55 const newNode = getAddNodesParams(
46 56 position,
47 57 {
48 58 ...new RuleChainConfig(),
49   - data: { name },
  59 + data: { name, configuration: { ruleChainId } },
50 60 },
51 61 {
52 62 id,
... ...
... ... @@ -65,6 +65,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
65 65 minZoom: 1,
66 66 panOnScroll: true,
67 67 selectionMode: SelectionMode.Partial,
  68 +
68 69 nodeTypes: {
69 70 [NodeTypeEnum.CUSTOM]: markRaw(BasicNode) as NodeComponent,
70 71 },
... ... @@ -79,14 +80,16 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
79 80 y: 0,
80 81 },
81 82 isValidConnection(connection, elements) {
  83 + if (!elements.targetNode || !elements.sourceNode) return false;
  84 +
82 85 const validateList = [validateInputAndOutput];
83 86 const targetData = elements.targetNode.data as NodeData;
84 87
85 88 if (
86   - targetData.category?.validateConnection &&
87   - isFunction(targetData.category.validateConnection)
  89 + targetData.categoryConfig?.validateConnection &&
  90 + isFunction(targetData.categoryConfig.validateConnection)
88 91 )
89   - validateList.push(targetData.category?.validateConnection);
  92 + validateList.push(targetData.categoryConfig?.validateConnection);
90 93
91 94 if (targetData.config?.validateConnection && isFunction(targetData.config.validateConnection))
92 95 validateList.push(targetData.config.validateConnection);
... ...
... ... @@ -47,7 +47,6 @@
47 47
48 48 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
49 49 resetFields();
50   - console.log(value);
51 50 setFieldsValue({
52 51 ...value,
53 52 ...(value?.[RelatedDeviceAttributeFieldsEnum.DEVICE_RELATIONS_QUERY] || {}),
... ...
... ... @@ -14,6 +14,7 @@ export const RuleChainConfig: NodeItemConfigType = {
14 14 clazz: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode',
15 15 categoryType: RuleNodeTypeEnum.FLOW,
16 16 name: 'rule chain',
  17 + // backgroundColor: '#d6c4f1',
17 18 configurationDescriptor: {
18 19 nodeDefinition: {
19 20 details:
... ...
... ... @@ -14,7 +14,7 @@
14 14 const handleClick = () => {
15 15 const { data } = props.nodeProps?.data || ({} as NodeData);
16 16 const { configuration } = (data || {}) as { configuration: Record<'ruleChainId', string> };
17   - if (configuration.ruleChainId) {
  17 + if (configuration?.ruleChainId) {
18 18 ROUTER.push(`/rule/chain/${configuration.ruleChainId}`);
19 19 }
20 20 };
... ...
... ... @@ -36,8 +36,8 @@
36 36
37 37 const getIcon = computed(() => {
38 38 const { icon } = unref(getNodeDefinition);
39   - const { category } = unref(getData);
40   - const { icon: categoryIcon } = category || {};
  39 + const { categoryConfig } = unref(getData);
  40 + const { icon: categoryIcon } = categoryConfig || {};
41 41 return icon || categoryIcon || 'tabler:circuit-ground';
42 42 });
43 43
... ... @@ -47,8 +47,8 @@
47 47 });
48 48
49 49 const getBackgroundColor = computed(() => {
50   - const { config, category } = unref(getData);
51   - const { backgroundColor: categoryBackgroundColor } = category || {};
  50 + const { config, categoryConfig } = unref(getData);
  51 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
52 52 const { backgroundColor } = config || {};
53 53 return backgroundColor || categoryBackgroundColor;
54 54 });
... ... @@ -60,8 +60,8 @@
60 60 <section class="text-dark-900 text-xs">
61 61 <p class="mb-0 font-bold"> {{ getData?.data?.name }} </p>
62 62 <p class="mb-0 mt-2 text-gray-500 italic">
63   - {{ getData.category?.title }}
64   - {{ getData?.category?.title && getData.config?.name ? '-' : '' }}
  63 + {{ getData.categoryConfig?.title }}
  64 + {{ getData?.categoryConfig?.title && getData.config?.name ? '-' : '' }}
65 65 {{ getData.config?.name }}
66 66 </p>
67 67 <p class="mt-1 mb-0 text-gray-500">{{ getData.data?.description }}</p>
... ...
... ... @@ -48,7 +48,7 @@
48 48 v-for="config in getCurrentCategoryNode.components"
49 49 :key="config.clazz"
50 50 :config="config"
51   - :category="getCurrentCategoryNode.category"
  51 + :categoryConfig="getCurrentCategoryNode.category"
52 52 />
53 53 </body>
54 54 </section>
... ...
... ... @@ -5,7 +5,7 @@
5 5 import { CategoryConfigType, NodeItemConfigType } from '../../../types/node';
6 6
7 7 const props = defineProps<{
8   - category?: CategoryConfigType;
  8 + categoryConfig?: CategoryConfigType;
9 9 config?: NodeItemConfigType;
10 10 }>();
11 11
... ... @@ -20,7 +20,7 @@
20 20
21 21 const getIcon = computed(() => {
22 22 const { icon } = unref(getNodeDefinition);
23   - const { icon: categoryIcon } = props.category || {};
  23 + const { icon: categoryIcon } = props.categoryConfig || {};
24 24 return icon || categoryIcon || 'tabler:circuit-ground';
25 25 });
26 26
... ... @@ -30,9 +30,9 @@
30 30 });
31 31
32 32 const getBackgroundColor = computed(() => {
33   - const { config, category } = props;
  33 + const { config, categoryConfig } = props;
34 34 const { backgroundColor } = config || {};
35   - const { backgroundColor: categoryBackgroundColor } = category || {};
  35 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
36 36 return backgroundColor || categoryBackgroundColor;
37 37 });
38 38
... ... @@ -87,8 +87,8 @@
87 87 {{ config?.name }}
88 88 </span>
89 89 </div>
90   - <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -left-2 border-light-50"></div>
91   - <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -right-2 border-light-50"></div>
  90 + <div class="w-4 h-4 bg-gray-300 rounded-md border absolute -left-3 border-gray-500"></div>
  91 + <div class="w-4 h-4 bg-gray-300 rounded-md border absolute -right-3 border-gray-500"></div>
92 92 </main>
93 93 </Tooltip>
94 94 </template>
... ...
... ... @@ -64,8 +64,8 @@
64 64
65 65 const getNodeIcon = computed(() => {
66 66 const { nodeData } = props;
67   - const { category, config } = nodeData;
68   - const { icon: categoryIcon } = category || {};
  67 + const { categoryConfig, config } = nodeData;
  68 + const { icon: categoryIcon } = categoryConfig || {};
69 69 const { configurationDescriptor } = config || {};
70 70 const { nodeDefinition } = configurationDescriptor || {};
71 71 const { icon } = nodeDefinition || {};
... ... @@ -74,8 +74,8 @@
74 74 });
75 75
76 76 const getTitleBackgroundColor = computed(() => {
77   - const { category, config } = props.nodeData;
78   - const { backgroundColor: categoryBackgroundColor } = category || {};
  77 + const { categoryConfig, config } = props.nodeData;
  78 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
79 79 const { backgroundColor } = config || {};
80 80 return categoryBackgroundColor || backgroundColor;
81 81 });
... ...
... ... @@ -90,7 +90,7 @@
90 90 <template #title>
91 91 <h2 class="font-bold text-2xl truncate">{{ nodeData?.data?.name }}</h2>
92 92 <p class="mb-0 text-gray-700">
93   - <span> {{ nodeData?.category?.title }}</span>
  93 + <span> {{ nodeData?.categoryConfig?.title }}</span>
94 94 <span class="mx-1">-</span>
95 95 <span>{{ nodeData?.config?.name }}</span>
96 96 </p>
... ...
1 1 .vue-flow__handle {
2 2 width: 16px;
3 3 height: 16px;
  4 + border-radius: 6px;
  5 + background-color: #d1d5db;
  6 + border-color: #6b7280;
  7 +}
  8 +
  9 +.vue-flow__handle:hover{
  10 + background-color: #000;
4 11 }
5 12
6 13 .vue-flow__handle-right {
7   - right: -8px;
  14 + right: -12px;
8 15 }
9 16
10 17 .vue-flow__handle-left {
11   - left: -8px;
  18 + left: -12px;
12 19 }
13 20
14 21 .vue-flow__background.vue-flow__container {
... ...
... ... @@ -134,7 +134,7 @@ export interface DragTransferData {
134 134 }
135 135
136 136 export interface NodeData<T = BasicNodeFormData> {
137   - category?: CategoryConfigType;
  137 + categoryConfig?: CategoryConfigType;
138 138 config?: NodeItemConfigType;
139 139 data?: T;
140 140 created?: boolean;
... ...