浏览代码

context menu for "new instance"

Martin Kunz 1 年之前
父节点
当前提交
ca2d8d8dff
共有 4 个文件被更改,包括 92 次插入29 次删除
  1. 66 0
      src/components/TheContextMenu.vue
  2. 8 14
      src/components/TheDynamics.vue
  3. 10 7
      src/components/TheModeler.vue
  4. 8 8
      src/components/TreeItem.vue

+ 66 - 0
src/components/TheContextMenu.vue

@@ -0,0 +1,66 @@
+<script setup lang="ts">
+import { NodeId, coerceNodeId } from '@/ua/NodeId';
+import { UABaseNode } from '@/ua/UABaseNode';
+import { useStore } from '@/util/store';
+import { storeToRefs } from 'pinia';
+import { ref, reactive, onMounted, onUnmounted, computed, type CSSProperties } from 'vue';
+
+const isVisible = ref(false);
+const menuPosition = reactive({ top: "0px", left: "0px" });
+const store = useStore();
+const { selectedNode } = storeToRefs(store);
+
+const openMenu = (event: any) => {
+    isVisible.value = true;
+    menuPosition.top = event.layerY + 'px';
+    menuPosition.left = event.layerX + 'px';
+};
+
+const closeMenu = () => {
+    isVisible.value = false;
+};
+
+const newInstance = () => {
+    store.selectedNode = new UABaseNode({browseName: "new node", nodeId: coerceNodeId("ns=0;i=999999")});
+    closeMenu();
+};
+
+const menuStyle = computed<CSSProperties>(() => ({
+    position: "absolute",
+    top: menuPosition.top,
+    left: menuPosition.left,
+    zIndex: 5,
+}));
+
+const handleClickOutside = (event: any) => {
+    if (!event.target.closest('.context-menu')) {
+       closeMenu();
+    }
+};
+
+onMounted(() => {
+    document.addEventListener('click', handleClickOutside);
+});
+
+onUnmounted(() => {
+    document.removeEventListener('click', handleClickOutside);
+});
+</script>
+
+<style scoped>
+.context-menu {
+    border: 1px solid #ccc;
+    background-color: white;
+    padding: 10px;
+    box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
+}
+</style>
+
+<template>
+    <div @contextmenu.prevent="openMenu($event)" :style="{ position: 'absolute' }">
+        <slot></slot>
+        <div v-if="isVisible" :style="menuStyle" class="context-menu">
+            <button @click="newInstance">new instance</button>
+        </div>
+    </div>
+</template>

+ 8 - 14
src/components/TheDynamics.vue

@@ -105,24 +105,18 @@ function getComponents(nid:string):DynamicNode{
   return dynNode;
 }
 
-function getNodeVersion(nid:string, dynNode:DynamicNode){
+function getNodeVersion(nid:string){
   let node =  (store.addressSpace?.findNode(nid)) as UABaseNode;
+  if(!node) 
+    return "";
   let refs = node.references;
-  let res = "";
   refs.forEach((ref)=>{
-    if(ref.toNode.displayName == "NodeVersion"){
-      res= ref.toNode.nodeId.toString()
-      dynNode.nodeVersionId= res;
-      return res;
-      
-    }else if(ref.fromNode.displayName == "NodeVersion"){
-      res= ref.fromNode.nodeId.toString()
-      dynNode.nodeVersionId= res;
-      return res;
+    if(ref.toNode.displayName == "NodeVersion" && ref.isForward){
+      return ref.toNode.nodeId.toString()      
+    }else if(ref.fromNode.displayName == "NodeVersion" && !ref.isForward){
+      return ref.fromNode.nodeId.toString()
     }
   })
-  dynNode.nodeVersionId= res;
-  return res;
 }
 
 
@@ -207,7 +201,7 @@ const typedef = ref("")
             <span class="input-group-text" id="inputGroup-sizing-default">NodeVersion</span>
           </div>
           <input type="text" class="form-control" aria-label="Default"
-            aria-describedby="inputGroup-sizing-default" :value="getNodeVersion(selectedNode.nodeId.toString(), dynNode)" disabled> 
+            aria-describedby="inputGroup-sizing-default" :value="getNodeVersion(selectedNode.nodeId.toString())" disabled> 
         </div>
         <div class="input-group mb-3">
           <div class="input-group-prepend">

+ 10 - 7
src/components/TheModeler.vue

@@ -2,6 +2,7 @@
 import type { UABaseNode } from '@/ua/UABaseNode';
 import TreeItem from './TreeItem.vue'
 import {useStore} from '@/util/store'
+import TheContextMenu from './TheContextMenu.vue';
 const store = useStore()
 
 function selectNode(node: UABaseNode) {
@@ -11,16 +12,18 @@ function selectNode(node: UABaseNode) {
 
 <template>
   <div class="card">
-  <div class="card-body" v-if="store.addressSpace">
+  <div class="card-body" v-if="store.addressSpace" style="position: relative;">
     <h5 class="card-title">Addressspace</h5>
     <p class="card-text">
       <ul>
-        <TreeItem class="item" 
-          v-if="store.rootNode!=null" 
-          :model="store.rootNode" 
-          @select-node="(node) => selectNode(node)" 
-          :filter-func="(node:UABaseNode) => {return false}">
-        </TreeItem>
+        <TheContextMenu>
+          <TreeItem class="item" 
+            v-if="store.rootNode!=null" 
+            :model="store.rootNode" 
+            @select-node="(node) => selectNode(node)" 
+            :filter-func="(node:UABaseNode) => {return false}">
+          </TreeItem>
+        </TheContextMenu>
       </ul>
     </p>
   </div>

+ 8 - 8
src/components/TreeItem.vue

@@ -28,14 +28,14 @@ function toggle() {
       </span>
     </div>
     <ul v-show="isOpen" v-if="isFolder && model">
-      <TreeItem
-        class="item"
-        :filter-func = "$props.filterFunc"
-        @select-node="(node: UABaseNode) => $emit('selectNode', node)"
-        v-for="child in model.getChildren()"
-        v-bind:key="child.nodeId.toString()"
-        :model="child">
-      </TreeItem>
+        <TreeItem
+          class="item"
+          :filter-func = "$props.filterFunc"
+          @select-node="(node: UABaseNode) => $emit('selectNode', node)"
+          v-for="child in model.getChildren()"
+          v-bind:key="child.nodeId.toString()"
+          :model="child">
+        </TreeItem>
     </ul>
   </li>
 </template>