From f2d1b5615b05c002ec63db4df7164c9ed3ecc4a3 Mon Sep 17 00:00:00 2001
From: zhaowei <zhaowei>
Date: 星期二, 26 八月 2025 16:42:21 +0800
Subject: [PATCH] 1、优化产品结构树以及设备结构树、列表右键菜单重复右键后出现window菜单问题 2、优化产品结构树节点删除后的loading展示时机 3、优化产品以及设备结构树权限配置获取列表时的loading展示时机

---
 src/views/dnc/base/modules/ProductStructure/ProductStructureBaseContextMenu.vue     |    7 
 src/views/dnc/base/modules/DeviceStructure/Permission/AssignPermissionModal.vue     |    5 
 src/views/dnc/common/TableContextMenu.vue                                           |   11 
 src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue                |   50 +
 src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue     |  324 +++++++--------
 src/views/dnc/base/modules/DeviceStructure/DeviceStructureTreeContextMenu.vue       |   61 +-
 src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue                  |  538 +++++++++++++------------
 src/views/dnc/base/modules/ProductStructure/Permission/DepartPermissionTransfer.vue |    2 
 src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue    |  214 +++++----
 9 files changed, 638 insertions(+), 574 deletions(-)

diff --git a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
index 41d4ac9..c2db72b 100644
--- a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
+++ b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
@@ -22,9 +22,13 @@
           <a-tree blockNode show-icon :expandedKeys.sync="expandedKeys"
                   :selectedKeys="selectedKeys" :treeData="treeDataSource" :autoExpandParent="autoExpandParent"
                   @select="handleTreeSelect" @expand="handleTreeExpand" @rightClick="handleTreeRightClick">
-            <template slot="title" slot-scope="{ title, parentId, entity, key:treeKey,equipmentId,type}">
-              <DeviceStructureTreeContextMenu ref="contextMenuRef"
-                                              :treeParams="{title,treeKey,searchValue,equipmentId,entity,type,param:currentDeviceDocClassCode}"/>
+            <template slot="title" slot-scope="{ title }">
+              <span v-if="title.indexOf(searchValue) > -1">
+                {{ title.substr(0, title.indexOf(searchValue)) }}
+                <span class="replaceSearch">{{ searchValue }}</span>
+                {{ title.substr(title.indexOf(searchValue) + searchValue.length) }}
+              </span>
+              <span v-else>{{ title }}</span>
             </template>
 
             <a-icon slot="switcherIcon" type="down"/>
@@ -37,281 +41,309 @@
 
     <!--鏉冮檺閰嶇疆寮圭獥-->
     <AssignPermissionModal :currentTreeNodeInfo="rightClickSelected" @submitSuccess="getTreeDataByApi"/>
-    <!--浜у搧缁撴瀯鏍戝熀鏈彸閿彍鍗�(绌虹櫧澶勮Е鍙�)-->
+
+    <!--璁惧缁撴瀯鏍戞爲鑺傜偣鍙抽敭鑿滃崟锛堟爲鑺傜偣瑙﹀彂锛�-->
+    <device-structure-tree-context-menu ref="mainContextmenuRef" :treeParams="rightClickSelected"/>
+
+    <!--璁惧缁撴瀯鏍戝熀鏈彸閿彍鍗�(绌虹櫧澶勮Е鍙�)-->
     <DeviceStructureBaseContextMenu ref="baseContextmenuRef"/>
   </a-card>
 </template>
 
 <script>
-import dncApi from '@/api/dnc'
-import DeviceStructureTreeContextMenu from './DeviceStructureTreeContextMenu'
-import AssignPermissionModal from './Permission/AssignPermissionModal'
-import DeviceStructureBaseContextMenu
-  from '@views/dnc/base/modules/DeviceStructure/DeviceStructureBaseContextMenu.vue'
+  import dncApi from '@/api/dnc'
+  import DeviceStructureTreeContextMenu from './DeviceStructureTreeContextMenu'
+  import AssignPermissionModal from './Permission/AssignPermissionModal'
+  import DeviceStructureBaseContextMenu
+    from '@views/dnc/base/modules/DeviceStructure/DeviceStructureBaseContextMenu.vue'
 
-export default {
-  name: 'DeviceStructureTree',
-  components: {
-    DeviceStructureBaseContextMenu,
-    AssignPermissionModal,
-    DeviceStructureTreeContextMenu
-  },
-  data() {
-    return {
-      searchInput: '',
-      cardLoading: false,
-      loading: false,
-      treeDataSource: [],
-      selectedKeys: [],
-      expandedKeys: [],
-      beforeSearchExpandedKeys: [],
-      searchValue: '',
-      dataList: [],
-      autoExpandParent: true,
-      checkStrictly: true,
-      allTreeKeys: [],
-      currentSelected: {},
-      rightClickSelected: {},
-      currentDeviceDocClassCode: 'SEND',
-      url: {
-        delete: '/nc/product/delete'
-      }
-    }
-  },
-  created() {
-    this.getTreeDataByApi()
-    this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
-    this.$bus.$on('handleSwitchDeviceDocClassCode', this.setCurrentDeviceDocClassCode)
-  },
-  beforeDestroy() {
-    this.$bus.$off('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
-    this.$bus.$off('handleSwitchDeviceDocClassCode', this.setCurrentDeviceDocClassCode)
-  },
-  methods: {
-    // 璋冪敤鎺ュ彛鑾峰彇鏍戠殑鏁版嵁
-    getTreeDataByApi() {
-      this.loading = true
-      this.cardLoading = true
-      this.treeDataSource = []
-      dncApi.getDeviceTreeDataApi()
-        .then(res => {
-          if (res.success) {
-            this.dataList = []
-            this.allTreeKeys = []
-            this.treeDataSource = res.result
-            this.generateList(this.treeDataSource)
-            this.expandedKeys = this.beforeSearchExpandedKeys = this.allTreeKeys
-            this.$bus.$emit('sendDeviceTreeNodeInfo', this.treeDataSource[0])
-          } else {
-            this.$message.warn(res.message)
-          }
-        })
-        .finally(() => {
-          this.loading = false
-          this.cardLoading = false
-        })
+  export default {
+    name: 'DeviceStructureTree',
+    components: {
+      DeviceStructureBaseContextMenu,
+      AssignPermissionModal,
+      DeviceStructureTreeContextMenu
     },
-
-    /**
-     * 閫氳繃鍙充晶tab鏍忔墍鍦╝ctiveKey璁剧疆褰撳墠璁惧鐨凬C鏂囨。绫诲瀷
-     * @param documentActiveTabKey 鍙充晶tab鏍忕殑activeKey
-     */
-    setCurrentDeviceDocClassCode(documentActiveTabKey) {
-      if (documentActiveTabKey === 1) this.currentDeviceDocClassCode = 'SEND'
-      else this.currentDeviceDocClassCode = 'REC'
-    },
-
-    /**
-     * 鏍戣妭鐐归�変腑鏃惰Е鍙�
-     * @param selectedKeys 閫変腑鑺傜偣key
-     * @param {node} node 鑺傜偣瀵硅薄
-     */
-    handleTreeSelect(selectedKeys, { node }) {
-      let record = node.dataRef
-      this.currentSelected = Object.assign({}, record)
-      // 鍚戝彸渚х埗绾х粍浠跺彂閫佸綋鍓嶉�変腑鏍戣妭鐐逛俊鎭�
-      this.$bus.$emit('sendDeviceTreeNodeInfo', this.currentSelected)
-      if (selectedKeys.length === 0) return
-      this.selectedKeys = selectedKeys
-    },
-
-    /**
-     * 鏍戣妭鐐瑰彸閿崟鍑昏妭鐐规椂瑙﹀彂
-     * @param event 浜嬩欢瀵硅薄
-     * @param node 鑺傜偣瀵硅薄
-     */
-    handleTreeRightClick({ event, node }) {
-      if (this.$refs.baseContextmenuRef) this.$refs.baseContextmenuRef.menuVisible = false
-      const record = node.dataRef
-      // 鑻ュ彸閿椂褰撳墠鍙充晶灞曠ず灞傜骇涓鸿澶囧眰绾т笖褰撳墠鍙抽敭鏍戝眰绾у悓涓鸿澶囧眰绾ф椂鍒欏湪瑙﹀彂鍙抽敭鑿滃崟鍔熻兘鏃跺悓鏃惰Е鍙戝乏閿�変腑鍔熻兘
-      if (this.currentSelected.type === 2 && record.type === 2) this.handleTreeSelect([record.key], { node })
-      this.rightClickSelected = Object.assign({}, record)
-    },
-
-    /**
-     * 鏍戣妭鐐瑰睍寮�鍚堝苟鏃惰Е鍙�
-     * @param expandedKeys 灞曞紑椤筴ey
-     */
-    handleTreeExpand(expandedKeys) {
-      this.expandedKeys = this.beforeSearchExpandedKeys = expandedKeys
-      console.log('beforeSearchExpandedKeys', this.beforeSearchExpandedKeys)
-      this.autoExpandParent = false
-    },
-
-    /* 杈撳叆鏌ヨ鍐呭鍙樺寲鏃惰Е鍙� */
-    handleSearchInputChange() {
-      let search = this.searchInput
-      let expandedKeys
-      let autoExpandParent
-      if (search !== '') {
-        expandedKeys = this.dataList
-          .map(item => {
-            if (item.title != null) {
-              if (item.title.indexOf(search) > -1) {
-                return this.getParentKey(item.key, this.treeDataSource)
-              }
-              return null
-            }
-          })
-          .filter((item, i, self) => item && self.indexOf(item) === i)
-        autoExpandParent = true
-      } else {
-        expandedKeys = this.beforeSearchExpandedKeys
-        autoExpandParent = false
-      }
-
-      Object.assign(this, {
-        expandedKeys,
-        searchValue: search,
-        autoExpandParent
-      })
-    },
-
-    /**
-     * 閫掑綊鑾峰緱杈撳叆椤圭殑鐖剁骇key
-     * @param key 瀛愰」key
-     * @param tree 瀛愰」
-     */
-    getParentKey(key, tree) {
-      let parentKey
-      for (let i = 0; i < tree.length; i++) {
-        const node = tree[i]
-        if (node.children) {
-          if (node.children.some(item => item.key === key)) {
-            parentKey = node.key
-          } else if (
-            this.getParentKey(key, node.children)) {
-            parentKey = this.getParentKey(key, node.children)
-          }
+    data() {
+      return {
+        searchInput: '',
+        cardLoading: false,
+        loading: false,
+        treeDataSource: [],
+        selectedKeys: [],
+        expandedKeys: [],
+        beforeSearchExpandedKeys: [],
+        searchValue: '',
+        dataList: [],
+        autoExpandParent: true,
+        checkStrictly: true,
+        allTreeKeys: [],
+        currentSelected: {},
+        rightClickSelected: {},
+        currentDeviceDocClassCode: 'SEND',
+        url: {
+          delete: '/nc/product/delete'
         }
       }
-      return parentKey
     },
-
-    /**
-     * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
-     * @param data
-     */
-    generateList(data) {
-      for (let i = 0; i < data.length; i++) {
-        const node = data[i]
-        const key = node.key
-        const title = node.title
-        this.dataList.push({ key, title })
-        this.allTreeKeys.push(key)
-        this.setTreeNodeIcon(node)
-        if (node.children) this.generateList(node.children)
-      }
-    },
-
-    /**
-     * 鏍戞墍鍦ㄧ埗鍏冪礌鐨勫彸閿簨浠�
-     * @param event 浜嬩欢瀵硅薄
-     */
-    openBaseContextMenu(event) {
-      event.preventDefault()
-      if (event.target.id !== 'tree-container') return
-      this.$refs.baseContextmenuRef.menuStyle.top = event.clientY + 'px'
-      this.$refs.baseContextmenuRef.menuStyle.left = event.clientX + 'px'
-      this.$refs.baseContextmenuRef.menuVisible = true
-      document.body.addEventListener('click', this.handleBaseContextMenuClose)
-    },
-
-    /**
-     * 璁剧疆鏍戣妭鐐瑰浘鏍�
-     * @param treeNode
-     */
-    setTreeNodeIcon(treeNode) {
-      if (+treeNode.type === 1) {
-        treeNode.slots = { icon: 'workshop' }
-      } else {
-        treeNode.slots = { icon: 'device' }
-      }
-    },
-
-    // 鎺у埗鍩虹鍙抽敭鑿滃崟鍏抽棴
-    handleBaseContextMenuClose() {
-      this.$refs.baseContextmenuRef.menuVisible = false
-      document.body.removeEventListener('click', this.handleBaseContextMenuClose)
-    },
-
-    // 鍒锋柊閲嶆柊鑾峰彇鏍戠殑鏁版嵁
-    handleTreeReload() {
+    created() {
       this.getTreeDataByApi()
+      this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+      this.$bus.$on('handleSwitchDeviceDocClassCode', this.setCurrentDeviceDocClassCode)
     },
+    beforeDestroy() {
+      this.$bus.$off('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+      this.$bus.$off('handleSwitchDeviceDocClassCode', this.setCurrentDeviceDocClassCode)
+    },
+    methods: {
+      // 璋冪敤鎺ュ彛鑾峰彇鏍戠殑鏁版嵁
+      getTreeDataByApi() {
+        this.loading = true
+        this.cardLoading = true
+        this.treeDataSource = []
+        dncApi.getDeviceTreeDataApi()
+          .then(res => {
+            if (res.success) {
+              this.dataList = []
+              this.allTreeKeys = []
+              this.treeDataSource = res.result
+              this.generateList(this.treeDataSource)
+              this.expandedKeys = this.beforeSearchExpandedKeys = this.allTreeKeys
+              this.$bus.$emit('sendDeviceTreeNodeInfo', this.treeDataSource[0])
+            } else {
+              this.$message.warn(res.message)
+            }
+          })
+          .finally(() => {
+            this.loading = false
+            this.cardLoading = false
+          })
+      },
 
-    triggerCorrespondingMethod({ methodName }) {
-      if (this[methodName]) this[methodName]()
+      /**
+       * 閫氳繃鍙充晶tab鏍忔墍鍦╝ctiveKey璁剧疆褰撳墠璁惧鐨凬C鏂囨。绫诲瀷
+       * @param documentActiveTabKey 鍙充晶tab鏍忕殑activeKey
+       */
+      setCurrentDeviceDocClassCode(documentActiveTabKey) {
+        if (documentActiveTabKey === 1) this.currentDeviceDocClassCode = 'SEND'
+        else this.currentDeviceDocClassCode = 'REC'
+      },
+
+      /**
+       * 鏍戣妭鐐归�変腑鏃惰Е鍙�
+       * @param selectedKeys 閫変腑鑺傜偣key
+       * @param {node} node 鑺傜偣瀵硅薄
+       */
+      handleTreeSelect(selectedKeys, { node }) {
+        let record = node.dataRef
+        this.currentSelected = Object.assign({}, record)
+        // 鍚戝彸渚х埗绾х粍浠跺彂閫佸綋鍓嶉�変腑鏍戣妭鐐逛俊鎭�
+        this.$bus.$emit('sendDeviceTreeNodeInfo', this.currentSelected)
+        if (selectedKeys.length === 0) return
+        this.selectedKeys = selectedKeys
+      },
+
+      /**
+       * 鏍戣妭鐐瑰彸閿崟鍑昏妭鐐规椂瑙﹀彂
+       * @param event 浜嬩欢瀵硅薄
+       * @param node 鑺傜偣瀵硅薄
+       */
+      handleTreeRightClick({ event, node }) {
+        if (this.$refs.baseContextmenuRef) this.$refs.baseContextmenuRef.menuVisible = false
+        const record = node.dataRef
+        // 鑻ュ彸閿椂褰撳墠鍙充晶灞曠ず灞傜骇涓鸿澶囧眰绾т笖褰撳墠鍙抽敭鏍戝眰绾у悓涓鸿澶囧眰绾ф椂鍒欏湪瑙﹀彂鍙抽敭鑿滃崟鍔熻兘鏃跺悓鏃惰Е鍙戝乏閿�変腑鍔熻兘
+        if (this.currentSelected.type === 2 && record.type === 2) this.handleTreeSelect([record.key], { node })
+        this.rightClickSelected = Object.assign({ param: this.currentDeviceDocClassCode }, record)
+        this.openMainContextMenu(event)
+      },
+
+      /**
+       * 鏍戣妭鐐瑰睍寮�鍚堝苟鏃惰Е鍙�
+       * @param expandedKeys 灞曞紑椤筴ey
+       */
+      handleTreeExpand(expandedKeys) {
+        this.expandedKeys = this.beforeSearchExpandedKeys = expandedKeys
+        this.autoExpandParent = false
+      },
+
+      /* 杈撳叆鏌ヨ鍐呭鍙樺寲鏃惰Е鍙� */
+      handleSearchInputChange() {
+        let search = this.searchInput
+        let expandedKeys
+        let autoExpandParent
+        if (search !== '') {
+          expandedKeys = this.dataList
+            .map(item => {
+              if (item.title != null) {
+                if (item.title.indexOf(search) > -1) {
+                  return this.getParentKey(item.key, this.treeDataSource)
+                }
+                return null
+              }
+            })
+            .filter((item, i, self) => item && self.indexOf(item) === i)
+          autoExpandParent = true
+        } else {
+          expandedKeys = this.beforeSearchExpandedKeys
+          autoExpandParent = false
+        }
+
+        Object.assign(this, {
+          expandedKeys,
+          searchValue: search,
+          autoExpandParent
+        })
+      },
+
+      /**
+       * 閫掑綊鑾峰緱杈撳叆椤圭殑鐖剁骇key
+       * @param key 瀛愰」key
+       * @param tree 瀛愰」
+       */
+      getParentKey(key, tree) {
+        let parentKey
+        for (let i = 0; i < tree.length; i++) {
+          const node = tree[i]
+          if (node.children) {
+            if (node.children.some(item => item.key === key)) {
+              parentKey = node.key
+            } else if (
+              this.getParentKey(key, node.children)) {
+              parentKey = this.getParentKey(key, node.children)
+            }
+          }
+        }
+        return parentKey
+      },
+
+      /**
+       * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
+       * @param data
+       */
+      generateList(data) {
+        for (let i = 0; i < data.length; i++) {
+          const node = data[i]
+          const key = node.key
+          const title = node.title
+          this.dataList.push({ key, title })
+          this.allTreeKeys.push(key)
+          this.setTreeNodeIcon(node)
+          if (node.children) this.generateList(node.children)
+        }
+      },
+
+      /**
+       * 鎵撳紑鏍戣妭鐐硅彍鍗曚簨浠�
+       * @param event 鏍戣妭鐐逛簨浠跺璞�
+       */
+      openMainContextMenu(event) {
+        this.$refs.mainContextmenuRef.menuStyle.top = event.clientY + 'px'
+        this.$refs.mainContextmenuRef.menuStyle.left = event.clientX + 'px'
+        this.$refs.mainContextmenuRef.menuVisible = true
+        document.body.addEventListener('click', this.handleMainContextMenuClose)
+      },
+
+      /**
+       * 鏍戞墍鍦ㄧ埗鍏冪礌鐨勫彸閿簨浠�
+       * @param event 浜嬩欢瀵硅薄
+       */
+      openBaseContextMenu(event) {
+        event.preventDefault()
+        if (event.target.id !== 'tree-container') return
+        if (this.$refs.mainContextmenuRef) this.$refs.mainContextmenuRef.menuVisible = false
+        this.$refs.baseContextmenuRef.menuStyle.top = event.clientY + 'px'
+        this.$refs.baseContextmenuRef.menuStyle.left = event.clientX + 'px'
+        this.$refs.baseContextmenuRef.menuVisible = true
+        document.body.addEventListener('click', this.handleBaseContextMenuClose)
+      },
+
+      /**
+       * 璁剧疆鏍戣妭鐐瑰浘鏍�
+       * @param treeNode
+       */
+      setTreeNodeIcon(treeNode) {
+        if (+treeNode.type === 1) {
+          treeNode.slots = { icon: 'workshop' }
+        } else {
+          treeNode.slots = { icon: 'device' }
+        }
+      },
+
+      // 鎺у埗涓昏鍙抽敭鑿滃崟鍏抽棴
+      handleMainContextMenuClose() {
+        if (this.$refs.mainContextmenuRef) this.$refs.mainContextmenuRef.menuVisible = false
+        document.body.removeEventListener('click', this.handleMainContextMenuClose)
+      },
+
+      // 鎺у埗鍩虹鍙抽敭鑿滃崟鍏抽棴
+      handleBaseContextMenuClose() {
+        this.$refs.baseContextmenuRef.menuVisible = false
+        document.body.removeEventListener('click', this.handleBaseContextMenuClose)
+      },
+
+      // 鍒锋柊閲嶆柊鑾峰彇鏍戠殑鏁版嵁
+      handleTreeReload() {
+        this.getTreeDataByApi()
+      },
+
+      triggerCorrespondingMethod({ methodName }) {
+        if (this[methodName]) this[methodName]()
+      }
     }
   }
-}
 </script>
 
 <style lang="less" scoped>
-/deep/ .ant-card-body {
-  padding: 0 12px 0 0;
-}
-
-/deep/ .ant-card-body, /deep/ .ant-spin-nested-loading, /deep/ .ant-spin-container {
-  height: 100%;
-}
-
-/deep/ .ant-tree-title, .ant-tree-title .ant-dropdown-trigger {
-  display: inline-block;
-  width: calc(100% - 24px) !important;
-}
-
-::-webkit-scrollbar {
-  width: 8px;
-}
-
-@media screen and (min-width: 1920px) {
-  .tree_con {
-    height: 748px !important;
+  /deep/ .ant-card-body {
+    padding: 0 12px 0 0;
   }
-}
 
-@media screen and (min-width: 1680px) and (max-width: 1920px) {
-  .tree_con {
-    height: 748px !important;
+  /deep/ .ant-card-body, /deep/ .ant-spin-nested-loading, /deep/ .ant-spin-container {
+    height: 100%;
   }
-}
 
-@media screen and (min-width: 1400px) and (max-width: 1680px) {
-  .tree_con {
-    height: 600px !important;
+  /deep/ .ant-tree-title, .ant-tree-title .ant-dropdown-trigger {
+    display: inline-block;
+    width: calc(100% - 24px) !important;
   }
-}
 
-@media screen and (min-width: 1280px) and (max-width: 1400px) {
-  .tree_con {
-    height: 501px !important;
+  ::-webkit-scrollbar {
+    width: 8px;
   }
-}
 
-@media screen and (max-width: 1280px) {
-  .tree_con {
-    height: 501px !important;
+  .replaceSearch {
+    color: #40a9ff;
+    font-weight: bold;
+    background-color: rgb(204, 204, 204);
   }
-}
+
+  @media screen and (min-width: 1920px) {
+    .tree_con {
+      height: 748px !important;
+    }
+  }
+
+  @media screen and (min-width: 1680px) and (max-width: 1920px) {
+    .tree_con {
+      height: 748px !important;
+    }
+  }
+
+  @media screen and (min-width: 1400px) and (max-width: 1680px) {
+    .tree_con {
+      height: 600px !important;
+    }
+  }
+
+  @media screen and (min-width: 1280px) and (max-width: 1400px) {
+    .tree_con {
+      height: 501px !important;
+    }
+  }
+
+  @media screen and (max-width: 1280px) {
+    .tree_con {
+      height: 501px !important;
+    }
+  }
 </style>
diff --git a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTreeContextMenu.vue b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTreeContextMenu.vue
index 58082fe..86ab05d 100644
--- a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTreeContextMenu.vue
+++ b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTreeContextMenu.vue
@@ -1,18 +1,12 @@
 <template>
-  <a-dropdown :trigger="['contextmenu']">
-    <span v-if="treeParams.title.indexOf(treeParams.searchValue) > -1">{{ treeParams.title.substr(0, treeParams.title.indexOf(treeParams.searchValue)) }}<span
-      class="replaceSearch">{{ treeParams.searchValue }}</span>{{ treeParams.title.substr(treeParams.title.indexOf(treeParams.searchValue) + treeParams.searchValue.length) }}</span>
-    <span v-else>{{ treeParams.title }}</span>
-    <template #overlay>
-      <a-menu @click="({ key: menuKey }) => onContextMenuClick(treeParams.treeKey, menuKey)"
-              @contextmenu="event=>event.preventDefault()">
-        <a-menu-item v-for="item in defaultContextMenuList[getCurrentMenuLevel]" :key="item.code" v-has="item.code">
-          <a-icon :type="item.icon"/>
-          {{item.label}}
-        </a-menu-item>
-      </a-menu>
+  <a-menu :style="menuStyle" @click="menuItemClick" v-if="menuVisible" mode="vertical" @contextmenu="menuContextMenu">
+    <template v-for="menuItem in defaultContextMenuList[getCurrentMenuLevel]">
+      <a-menu-item :key="menuItem.code" v-has="menuItem.code">
+        <a-icon :type="menuItem.icon"/>
+        {{ menuItem.label }}
+      </a-menu-item>
     </template>
-  </a-dropdown>
+  </a-menu>
 </template>
 
 <script>
@@ -26,6 +20,15 @@
     },
     data() {
       return {
+        menuVisible: false,
+        menuStyle: {
+          position: 'fixed',
+          top: 0,
+          left: 0,
+          border: '1px solid #eee',
+          boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
+          zIndex: 999
+        },
         defaultContextMenuList: {
           //杞﹂棿
           workshop: [
@@ -41,16 +44,15 @@
     },
     computed: {
       getCurrentMenuLevel() {
-        if (this.treeParams.type===1) return 'workshop'
+        if (this.treeParams.type === 1) return 'workshop'
         else return 'device'
       }
     },
     methods: {
-      onContextMenuClick(treeKey, menuKey) {
+      menuItemClick({ key }) {
+        const menuKey = key
         const level = this.getCurrentMenuLevel
-        const { param } = this.treeParams
-        const treeNodeInfo = Object.assign({}, this.treeParams, { param })
-        console.log('treeNodeInfo++++++++++++++++++++++++++++', treeNodeInfo)
+        const treeNodeInfo = Object.assign({ treeKey: this.treeParams.key }, this.treeParams)
         if (treeNodeInfo.type === 2) treeNodeInfo.type = 7
         // 璁惧缁撴瀯鏍戣妭鐐逛腑鐨勮澶囧眰绾т负2锛屼絾鍦ㄤ骇鍝佺粨鏋勬爲涓皢璁惧灞傜骇鐨則ype璁剧疆涓�7锛屽洜姝ゅ湪姝ゅ璁剧疆涓�7
         const menuKeyArray = menuKey.split('_')
@@ -63,21 +65,18 @@
         } else {
           methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).join('')
         }
-        console.log('methodName------------------------------------', methodName)
-        console.log('treeParams------------------------------------', this.treeParams)
         const modalTitle = this.defaultContextMenuList[level].find(item => item.code === menuKey).label
-
         this.$bus.$emit('treeMenuItemMethodTrigger', { methodName, modalTitle, treeNodeInfo })
+      },
+
+      /**
+       * 閬垮厤鍗曟閲嶅鍙抽敭鍚庡叧闂彍鍗曟垨鎵撳紑window鑿滃崟
+       * @param event 浜嬩欢瀵硅薄
+       */
+      menuContextMenu(event) {
+        event.preventDefault()
+        event.stopPropagation()
       }
     }
   }
-</script>
-
-<style scoped>
-  .replaceSearch {
-    color: #40a9ff;
-    font-weight: bold;
-    background-color: rgb(204, 204, 204);
-  }
-
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/DeviceStructure/Permission/AssignPermissionModal.vue b/src/views/dnc/base/modules/DeviceStructure/Permission/AssignPermissionModal.vue
index 6ea023b..c11061f 100644
--- a/src/views/dnc/base/modules/DeviceStructure/Permission/AssignPermissionModal.vue
+++ b/src/views/dnc/base/modules/DeviceStructure/Permission/AssignPermissionModal.vue
@@ -66,6 +66,11 @@
 
       // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夌敤鎴峰垪琛�
       getAllUsersListByApi() {
+        this.$nextTick(() => {
+          this.$refs.userPermissionTransferRef.spinning = true
+          this.allUsersList = []
+          this.$refs.userPermissionTransferRef.targetKeys = []
+        })
         dncApi.getAllUsersListApi()
           .then(res => {
             if (res.success) {
diff --git a/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue b/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
index 425111e..464e7df 100644
--- a/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
@@ -27,123 +27,131 @@
 </template>
 
 <script>
-import dncApi from '@/api/dnc'
-import DepartPermissionTransfer from './DepartPermissionTransfer'
-import UserPermissionTransfer from './UserPermissionTransfer'
+  import dncApi from '@/api/dnc'
+  import DepartPermissionTransfer from './DepartPermissionTransfer'
+  import UserPermissionTransfer from './UserPermissionTransfer'
 
-export default {
-  name: 'AssignPermissionModal',
-  components: { UserPermissionTransfer, DepartPermissionTransfer },
-  props: {
-    currentTreeNodeInfo: {
-      type: Object
-    }
-  },
-  data() {
-    return {
-      visible: false,
-      title: '',
-      isAssignSonNode: true,
-      activeTabKey: 1,
-      allDepartmentsList: [],
-      allUsersList: [],
-      allTreeKeys: [],
-      hasLoadedDataTabKeyArray: []
-    }
-  },
-  watch: {
-    visible: {
-      handler(value) {
-        if (value) {
-          this.activeTabKey = 1
-          this.isAssignSonNode = true
-          this.getAllDepartmentsListByApi()
+  export default {
+    name: 'AssignPermissionModal',
+    components: { UserPermissionTransfer, DepartPermissionTransfer },
+    props: {
+      currentTreeNodeInfo: {
+        type: Object
+      }
+    },
+    data() {
+      return {
+        visible: false,
+        title: '',
+        isAssignSonNode: true,
+        activeTabKey: 1,
+        allDepartmentsList: [],
+        allUsersList: [],
+        allTreeKeys: [],
+        hasLoadedDataTabKeyArray: []
+      }
+    },
+    watch: {
+      visible: {
+        handler(value) {
+          if (value) {
+            this.activeTabKey = 1
+            this.isAssignSonNode = true
+            this.getAllDepartmentsListByApi()
+          }
+        }
+      },
+      activeTabKey: {
+        handler(value) {
+          if (this.hasLoadedDataTabKeyArray.includes(value)) return
+          if (value === 2) this.getAllUsersListByApi()
+          this.hasLoadedDataTabKeyArray.push(value)
         }
       }
     },
-    activeTabKey: {
-      handler(value) {
-        if (this.hasLoadedDataTabKeyArray.includes(value)) return
-        if (value === 2) this.getAllUsersListByApi()
-        this.hasLoadedDataTabKeyArray.push(value)
-      }
-    }
-  },
-  created() {
-    this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
-  },
-  methods: {
-    // 鐐瑰嚮鏍戣妭鐐瑰彸閿彍鍗曟潈闄愰厤缃寜閽悗瑙﹀彂
-    handleAssignPermission() {
-      this.visible = true
+    created() {
+      this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
     },
+    methods: {
+      // 鐐瑰嚮鏍戣妭鐐瑰彸閿彍鍗曟潈闄愰厤缃寜閽悗瑙﹀彂
+      handleAssignPermission() {
+        this.visible = true
+      },
 
-    // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夎溅闂村垪琛�
-    getAllDepartmentsListByApi() {
-      this.allTreeKeys = []
-      dncApi.getAllDepartmentsListApi()
-        .then(res => {
-          if (res.success) {
-            this.allDepartmentsList = res.result
-            this.generateList(this.allDepartmentsList)
-            this.$nextTick(() => {
-              this.$refs.departPermissionTransferRef.getHasPermissionDepartByApi()
-              this.$refs.departPermissionTransferRef.expandedKeys = this.allTreeKeys
-              this.$refs.departPermissionTransferRef.flatten(JSON.parse(JSON.stringify(this.allDepartmentsList)))
-            })
-            // 鍙湁涓婃閫�鍑烘椂鍦ㄨ溅闂村垎閰峵ab鐣岄潰鎵嶄細杩涘叆姝ゅ垽鏂�
-            // 鑻ヤ笂娆¢��鍑烘椂鍦ㄧ敤鎴峰垎閰峵ab鐣岄潰鍒欏啀娆¤繘鍏ユ椂key鐢�2鍙樹负1鏃朵細瑙﹀彂watch鐩戞祴activeTabKey鍙樺寲鍒欎細灏唊ey:1鍔犲叆hasLoadedDataTabKeyArray锛屽洜姝ゆ棤闇�鍐嶆鍔犲叆key:1
-            if (!this.hasLoadedDataTabKeyArray.includes(this.activeTabKey)) this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
-          }
+      // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夎溅闂村垪琛�
+      getAllDepartmentsListByApi() {
+        this.allTreeKeys = this.allDepartmentsList = []
+        this.$nextTick(() => {
+          this.$refs.departPermissionTransferRef.targetKeys = this.$refs.departPermissionTransferRef.dataSource = []
+          this.$refs.departPermissionTransferRef.spinning = true
         })
-    },
+        dncApi.getAllDepartmentsListApi()
+          .then(res => {
+            if (res.success) {
+              this.allDepartmentsList = res.result
+              this.generateList(this.allDepartmentsList)
+              this.$nextTick(() => {
+                this.$refs.departPermissionTransferRef.getHasPermissionDepartByApi()
+                this.$refs.departPermissionTransferRef.expandedKeys = this.allTreeKeys
+                this.$refs.departPermissionTransferRef.flatten(JSON.parse(JSON.stringify(this.allDepartmentsList)))
+              })
+              // 鍙湁涓婃閫�鍑烘椂鍦ㄨ溅闂村垎閰峵ab鐣岄潰鎵嶄細杩涘叆姝ゅ垽鏂�
+              // 鑻ヤ笂娆¢��鍑烘椂鍦ㄧ敤鎴峰垎閰峵ab鐣岄潰鍒欏啀娆¤繘鍏ユ椂key鐢�2鍙樹负1鏃朵細瑙﹀彂watch鐩戞祴activeTabKey鍙樺寲鍒欎細灏唊ey:1鍔犲叆hasLoadedDataTabKeyArray锛屽洜姝ゆ棤闇�鍐嶆鍔犲叆key:1
+              if (!this.hasLoadedDataTabKeyArray.includes(this.activeTabKey)) this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
+            }
+          })
+      },
 
-    /**
-     * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
-     * @param data
-     */
-    generateList(data) {
-      for (let i = 0; i < data.length; i++) {
-        const node = data[i]
-        const key = node.key
-        this.allTreeKeys.push(key)
-        if (node.children) this.generateList(node.children)
-      }
-    },
-
-    // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夌敤鎴峰垪琛�
-    getAllUsersListByApi() {
-      dncApi.getAllUsersListApi()
-        .then(res => {
-          if (res.success) {
-            this.allUsersList = res.result
-            this.$nextTick(() => this.$refs.userPermissionTransferRef.getHasPermissionUserByApi())
-          }
-        })
-    },
-
-    setAdminDisabled() {
-      this.allUsersList = this.allUsersList.map(item => {
-        return {
-          ...item,
-          disabled: item.username === 'admin'
+      /**
+       * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
+       * @param data
+       */
+      generateList(data) {
+        for (let i = 0; i < data.length; i++) {
+          const node = data[i]
+          const key = node.key
+          this.allTreeKeys.push(key)
+          if (node.children) this.generateList(node.children)
         }
-      })
-    },
+      },
 
-    handleModalClose() {
-      this.visible = false
-      this.hasLoadedDataTabKeyArray = []
-    },
+      // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夌敤鎴峰垪琛�
+      getAllUsersListByApi() {
+        this.$nextTick(() => {
+          this.allUsersList = []
+          this.$refs.userPermissionTransferRef.spinning = true
+        })
+        dncApi.getAllUsersListApi()
+          .then(res => {
+            if (res.success) {
+              this.allUsersList = res.result
+              this.$nextTick(() => this.$refs.userPermissionTransferRef.getHasPermissionUserByApi())
+            }
+          })
+      },
 
-    triggerCorrespondingMethod({ methodName, modalTitle }) {
-      if (this[methodName]) {
-        this[methodName]()
-        this.title = modalTitle
+      setAdminDisabled() {
+        this.allUsersList = this.allUsersList.map(item => {
+          return {
+            ...item,
+            disabled: item.username === 'admin'
+          }
+        })
+      },
+
+      handleModalClose() {
+        this.visible = false
+        this.hasLoadedDataTabKeyArray = []
+      },
+
+      triggerCorrespondingMethod({ methodName, modalTitle }) {
+        if (this[methodName]) {
+          this[methodName]()
+          this.title = modalTitle
+        }
       }
     }
   }
-}
 </script>
 
 <style scoped>
diff --git a/src/views/dnc/base/modules/ProductStructure/Permission/DepartPermissionTransfer.vue b/src/views/dnc/base/modules/ProductStructure/Permission/DepartPermissionTransfer.vue
index 7b7ee3d..c40a94e 100644
--- a/src/views/dnc/base/modules/ProductStructure/Permission/DepartPermissionTransfer.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Permission/DepartPermissionTransfer.vue
@@ -48,8 +48,6 @@
 <script>
   import dncApi from '@/api/dnc'
 
-  const transferDataSource = []
-
   export default {
     name: 'DepartPermissionTransfer',
     components: {},
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureBaseContextMenu.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureBaseContextMenu.vue
index c5a1cc2..2efd3ba 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureBaseContextMenu.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureBaseContextMenu.vue
@@ -18,11 +18,6 @@
 export default {
   name: 'ProductStructureBaseContextMenu',
   components: {},
-  props: {
-    tableRowInfo: {
-      type: Object
-    }
-  },
   data() {
     return {
       menuVisible: false,
@@ -34,7 +29,6 @@
         boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
         zIndex: 999
       },
-      currentMenuLevel: '',
       baseContextMenuList: [
         { label: '鍒锋柊', code: 'tree_reload', icon: 'reload', isHasPermission: false, isCommonMethod: false },
         { label: '娣诲姞浜у搧', code: 'product_add', icon: 'plus', isHasPermission: true, isCommonMethod: false }
@@ -43,7 +37,6 @@
   },
   methods: {
     menuItemClick({ key }) {
-      console.log('menuKey', key)
       const isCommonMethod = this.baseContextMenuList.find(item => item.code === key).isCommonMethod
       const modalTitle = this.baseContextMenuList.find(item => item.code === key).label
       const menuKeyArray = key.split('_')
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
index 2c72304..011fb44 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
@@ -30,8 +30,12 @@
                   :selectedKeys="selectedKeys" :treeData="treeDataSource" :autoExpandParent="autoExpandParent"
                   @select="handleTreeSelect" @expand="handleTreeExpand" @rightClick="handleTreeRightClick">
             <template slot="title" slot-scope="{ label, parentId, key:treeKey,type}">
-              <ProductStructureTreeContextMenu ref="contextMenuRef"
-                                               :treeParams="{label,treeKey,searchValue,type}"/>
+              <span v-if="label.indexOf(searchValue) > -1">
+                {{label.substr(0, label.indexOf(searchValue))}}
+                <span class="replaceSearch">{{searchValue}}</span>
+                {{label.substr(label.indexOf(searchValue) + searchValue.length)}}
+              </span>
+              <span v-else>{{ label }}</span>
             </template>
 
             <a-icon slot="switcherIcon" type="down"/>
@@ -68,6 +72,10 @@
                              @submitSuccess="modalFormSubmitSuccess"/>
     <!--寮曠敤閮ㄤ欢-->
     <NcComponentBorrowModal :currentBorrowInfo="rightClickSelected" @submitSuccess="modalFormSubmitSuccess"/>
+
+    <!--浜у搧缁撴瀯鏍戝彸閿彍鍗曪紙鏍戣妭鐐硅Е鍙戯級-->
+    <product-structure-tree-context-menu ref="mainContextmenuRef" :treeParams="rightClickSelected"/>
+
     <!--浜у搧缁撴瀯鏍戝熀鏈彸閿彍鍗�(绌虹櫧澶勮Е鍙�)-->
     <ProductStructureBaseContextMenu ref="baseContextmenuRef"/>
 
@@ -204,9 +212,10 @@
 
       /**
        * 鏍戣妭鐐瑰彸閿崟鍑昏妭鐐规椂瑙﹀彂
+       * @param event 浜嬩欢瀵硅薄
        * @param node 鑺傜偣瀵硅薄
        */
-      handleTreeRightClick({ node }) {
+      handleTreeRightClick({ event, node }) {
         if (this.$refs.baseContextmenuRef) this.$refs.baseContextmenuRef.menuVisible = false
         const that = this
         const record = node.dataRef
@@ -222,7 +231,9 @@
                   message: '娑堟伅',
                   description: '鏆傛棤璇ヨ妭鐐硅缁嗕俊鎭�'
                 })
+                return
               }
+              this.openMainContextMenu(event)
             } else {
               that.$notification.error({
                 message: '娑堟伅',
@@ -243,10 +254,7 @@
           okType: 'danger',
           cancelText: '鍙栨秷',
           onOk: () => {
-            if (!url.delete) {
-              this.$message.error('璇疯缃畊rl.delete灞炴��!')
-              return
-            }
+            that.loading = true
             deleteAction(url.delete + `/${id}/${type}`)
               .then((res) => {
                 if (res.success) {
@@ -272,7 +280,7 @@
         })
       },
 
-      // 鏍戣妭鐐瑰彸閿崟鍑昏彍鍗曚腑鍒犻櫎鎸夐挳鏃惰Е鍙�
+      // 鍙戦�乶c绋嬪簭鑷充笁缁村伐鑹�
       handleSendNcToPlm() {
         const that = this
         const { rightClickSelected: { id, type }, $confirm, url, $notification } = that
@@ -287,7 +295,7 @@
               this.$message.error('璇疯缃畊rl.webServiceDNCToPlm!')
               return
             }
-            getAction(url.webServiceDNCToPlm, {id: id})
+            getAction(url.webServiceDNCToPlm, { id: id })
               .then((res) => {
                 if (res.success) {
                   that.getTreeDataByApi()
@@ -446,12 +454,24 @@
       },
 
       /**
+       * 鎵撳紑鏍戣妭鐐硅彍鍗曚簨浠�
+       * @param event 鏍戣妭鐐逛簨浠跺璞�
+       */
+      openMainContextMenu(event) {
+        this.$refs.mainContextmenuRef.menuStyle.top = event.clientY + 'px'
+        this.$refs.mainContextmenuRef.menuStyle.left = event.clientX + 'px'
+        this.$refs.mainContextmenuRef.menuVisible = true
+        document.body.addEventListener('click', this.handleMainContextMenuClose)
+      },
+
+      /**
        * 鏍戞墍鍦ㄧ埗鍏冪礌鐨勫彸閿簨浠�
        * @param event 浜嬩欢瀵硅薄
        */
       openBaseContextMenu(event) {
         event.preventDefault()
         if (event.target.id !== 'tree-container') return
+        if (this.$refs.mainContextmenuRef) this.$refs.mainContextmenuRef.menuVisible = false
         this.$refs.baseContextmenuRef.menuStyle.top = event.clientY + 'px'
         this.$refs.baseContextmenuRef.menuStyle.left = event.clientX + 'px'
         this.$refs.baseContextmenuRef.menuVisible = true
@@ -484,6 +504,12 @@
             break
           default:
         }
+      },
+
+      // 鎺у埗涓昏鍙抽敭鑿滃崟鍏抽棴
+      handleMainContextMenuClose() {
+        if (this.$refs.mainContextmenuRef) this.$refs.mainContextmenuRef.menuVisible = false
+        document.body.removeEventListener('click', this.handleMainContextMenuClose)
       },
 
       // 鎺у埗鍩虹鍙抽敭鑿滃崟鍏抽棴
@@ -537,6 +563,12 @@
     align-items: center;
   }
 
+  .replaceSearch {
+    color: #40a9ff;
+    font-weight: bold;
+    background-color: rgb(204, 204, 204);
+  }
+
   @media screen and (min-width: 1920px) {
     .tree_con {
       height: 748px !important;
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
index 283f48b..a38332e 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
@@ -1,183 +1,171 @@
 <template>
-  <a-dropdown :trigger="['contextmenu']">
-    <span v-if="treeParams.label.indexOf(treeParams.searchValue) > -1">{{
-        treeParams.label.substr(0, treeParams.label.indexOf(treeParams.searchValue))
-      }}<span
-        class="replaceSearch">{{
-          treeParams.searchValue
-        }}</span>{{
-        treeParams.label.substr(treeParams.label.indexOf(treeParams.searchValue) + treeParams.searchValue.length)
-      }}</span>
-    <span v-else>{{ treeParams.label }}</span>
-    <template #overlay>
-      <a-menu @click="({ key: menuKey }) => onContextMenuClick(treeParams.treeKey, menuKey)"
-              @contextmenu="event=>event.preventDefault()">
-        <a-menu-item v-for="item in defaultContextMenuList[getCurrentMenuLevel]" :key="item.code" v-has="item.code">
-          <a-icon :type="item.icon"/>
-          {{ item.label }}
-        </a-menu-item>
-      </a-menu>
+  <a-menu :style="menuStyle" @click="menuItemClick" v-if="menuVisible" mode="vertical" @contextmenu="menuContextMenu">
+    <template v-for="menuItem in defaultContextMenuList[getCurrentMenuLevel]">
+      <a-menu-item :key="menuItem.code" v-has="menuItem.code">
+        <a-icon :type="menuItem.icon"/>
+        {{ menuItem.label }}
+      </a-menu-item>
     </template>
-  </a-dropdown>
+  </a-menu>
 </template>
 
 <script>
-export default {
-  name: 'ProductStructureTreeContextMenu',
-  components: {},
-  props: {
-    treeParams: {
-      type: Object
-    }
-  },
-  data() {
-    return {
-      defaultContextMenuList: {
-        //浜у搧
-        product: [
-          { label: '娣诲姞浜у搧', code: 'product_add', icon: 'plus', isCommonMethod: false },
-          { label: '娣诲姞閮ㄤ欢', code: 'product_add_child', icon: 'plus', isCommonMethod: false },
-          { label: '缂栬緫浜у搧淇℃伅', code: 'product_edit', icon: 'edit', isCommonMethod: false },
-          // {  label: '瀵煎嚭鏂囨。', code: 'product_export', icon: 'export', isCommonMethod: true },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'product_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'product_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'product_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '鍒犻櫎', code: 'product_delete', icon: 'delete', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-        ],
-        //閮ㄤ欢
-        component: [
-          { label: '娣诲姞瀛愰儴浠�', code: 'component_add', icon: 'plus', isCommonMethod: false },
-          { label: '娣诲姞闆朵欢', code: 'component_add_child', icon: 'plus', isCommonMethod: false },
-          { label: '鍒涘缓宸ュ簭', code: 'component_add_relative', icon: 'plus', isCommonMethod: false },
-          { label: '缂栬緫閮ㄤ欢淇℃伅', code: 'component_edit', icon: 'edit', isCommonMethod: false },
-          // {  label: '瀵煎嚭鏂囨。', code: 'component_export', icon: 'export', isCommonMethod: true },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'component_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'component_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'component_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '寮曠敤閮ㄤ欢',code: 'component_borrow',icon: 'plus',isCommonMethod: false},
-          { label: '鍒犻櫎', code: 'component_delete', icon: 'delete', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-        ],
-        //闆朵欢
-        part: [
-          { label: '娣诲姞闆朵欢', code: 'parts_add', icon: 'plus', isCommonMethod: false },
-          { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
-          // { label: '鍒涘缓宸ュ簭', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
-          { label: '缂栬緫闆朵欢淇℃伅', code: 'parts_edit', icon: 'edit', isCommonMethod: false },
-          // {  label: '瀵煎嚭鏂囨。', code: 'parts_export', icon: 'export', isCommonMethod: true },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'parts_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'parts_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'parts_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '鍒犻櫎', code: 'parts_delete', icon: 'delete', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-        ],
-        //宸ヨ壓瑙勭▼鐗堟湰
-        processSpecVersion: [
-          { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'version_add', icon: 'plus', isCommonMethod: false },
-          { label: '鍒涘缓宸ュ簭', code: 'version_add_child', icon: 'plus', isCommonMethod: false },
-          { label: '缂栬緫宸ヨ壓瑙勭▼鐗堟湰淇℃伅', code: 'version_edit', icon: 'edit', isCommonMethod: false },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'version_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'version_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'version_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '鍒犻櫎', code: 'version_delete', icon: 'delete', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-        ],
-        //宸ュ簭
-        process: [
-          { label: '鍒涘缓宸ュ簭', code: 'process_add', icon: 'plus', isCommonMethod: false },
-          { label: '鍒涘缓宸ユ', code: 'process_add_child', icon: 'plus', isCommonMethod: false },
-          { label: '娣诲姞璁惧绫�', code: 'process_add_type', icon: 'plus', isCommonMethod: true },
-          { label: '缂栬緫宸ュ簭淇℃伅', code: 'process_edit', icon: 'edit', isCommonMethod: false },
-          { label: '鍒犻櫎', code: 'process_delete', icon: 'delete', isCommonMethod: true },
-          // {  label: '瀵煎嚭NC绋嬪簭', code: 'process_export', icon: 'export', isCommonMethod: true },
-          { label: '瀵煎叆NC绋嬪簭', code: 'process_nc_import', icon: 'import', isCommonMethod: true },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'process_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'process_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'process_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true },
-          { label: '鍙戦�乶c绋嬪簭鑷充笁缁村伐鑹�', code: 'process_send_nc_to_plm', icon: 'import', isCommonMethod: true }
-        ],
-        //宸ユ
-        processStep: [
-          { label: '鍒涘缓宸ユ', code: 'processStep_add', icon: 'plus', isCommonMethod: false },
-          { label: '缂栬緫宸ユ淇℃伅', code: 'processStep_edit', icon: 'edit', isCommonMethod: false },
-          { label: '娣诲姞璁惧绫�', code: 'processStep_add_type', icon: 'plus', isCommonMethod: true },
-          { label: '鍒犻櫎', code: 'processStep_delete', icon: 'delete', isCommonMethod: true },
-          // {  label: '瀵煎嚭NC绋嬪簭', code: 'processStep_export', icon: 'import', isCommonMethod: true },
-          { label: '瀵煎叆NC绋嬪簭', code: 'processStep_nc_import', icon: 'import', isCommonMethod: true },
-          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'processStep_other_import', icon: 'import', isCommonMethod: true },
-          { label: '妫�绱㈢數瀛愭牱鏉�', code: 'processStep_search', icon: 'search', isCommonMethod: true },
-          { label: '妫�绱C鏂囦欢', code: 'processStep_search_nc', icon: 'search', isCommonMethod: true },
-          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true },
-          { label: '鍙戦�乶c绋嬪簭鑷充笁缁村伐鑹�', code: 'processStep_send_nc_to_plm', icon: 'import', isCommonMethod: true }
-        ]
-      }
-    }
-  },
-  computed: {
-    getCurrentMenuLevel() {
-      switch (+this.treeParams.type) {
-        case 1:
-          return 'product'
-        case 2:
-          return 'component'
-        case 3:
-          return 'part'
-        case 4:
-          return 'processSpecVersion'
-        case 5:
-          return 'process'
-        case 6:
-          return 'processStep'
+  export default {
+    name: 'ProductStructureTreeContextMenu',
+    components: {},
+    props: {
+      treeParams: {
+        type: Object
       }
     },
-    // getCurrentDocClassCode() {
-    //   switch (+this.treeParams.type) {
-    //     case 1:
-    //       return 'OTHER'
-    //     case 2:
-    //       return 'OTHER'
-    //     case 3:
-    //       return 'OTHER'
-    //     case 4:
-    //       return 'OTHER'
-    //     case 5:
-    //       return 'NC'
-    //     case 6:
-    //       return 'NC'
-    //   }
-    // }
-  },
-  methods: {
-    onContextMenuClick(treeKey, menuKey) {
-      const level = this.getCurrentMenuLevel
-      console.log('level---------------------', level)
-      const treeNodeInfo = Object.assign({}, this.treeParams)
-      console.log('treeNodeInfo******************', treeNodeInfo)
-      const menuKeyArray = menuKey.split('_')
-      const isCommonMethod = this.defaultContextMenuList[level].find(item => item.code === menuKey).isCommonMethod
-      // product_add => handleAdd 瑙﹀彂瀵瑰簲缁勪欢浜嬩欢
-      let methodName
-      // 鍒ゆ柇鏄惁涓哄叕鍏辨柟娉曪紝濡傛灉涓哄叕鍏辨柟娉曞垯鎴彇涓撴湁灞炴�roduct/component/part/process绛夊瓧娈�
-      if (isCommonMethod) {
-        methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).slice(1).join('')
-      } else {
-        methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).join('')
+    data() {
+      return {
+        menuVisible: false,
+        menuStyle: {
+          position: 'fixed',
+          top: 0,
+          left: 0,
+          border: '1px solid #eee',
+          boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
+          zIndex: 999
+        },
+        defaultContextMenuList: {
+          //浜у搧
+          product: [
+            { label: '娣诲姞浜у搧', code: 'product_add', icon: 'plus', isCommonMethod: false },
+            { label: '娣诲姞閮ㄤ欢', code: 'product_add_child', icon: 'plus', isCommonMethod: false },
+            { label: '缂栬緫浜у搧淇℃伅', code: 'product_edit', icon: 'edit', isCommonMethod: false },
+            // {  label: '瀵煎嚭鏂囨。', code: 'product_export', icon: 'export', isCommonMethod: true },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'product_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'product_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'product_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '鍒犻櫎', code: 'product_delete', icon: 'delete', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+          ],
+          //閮ㄤ欢
+          component: [
+            { label: '娣诲姞瀛愰儴浠�', code: 'component_add', icon: 'plus', isCommonMethod: false },
+            { label: '娣诲姞闆朵欢', code: 'component_add_child', icon: 'plus', isCommonMethod: false },
+            { label: '鍒涘缓宸ュ簭', code: 'component_add_relative', icon: 'plus', isCommonMethod: false },
+            { label: '缂栬緫閮ㄤ欢淇℃伅', code: 'component_edit', icon: 'edit', isCommonMethod: false },
+            // {  label: '瀵煎嚭鏂囨。', code: 'component_export', icon: 'export', isCommonMethod: true },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'component_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'component_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'component_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '寮曠敤閮ㄤ欢', code: 'component_borrow', icon: 'plus', isCommonMethod: false },
+            { label: '鍒犻櫎', code: 'component_delete', icon: 'delete', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+          ],
+          //闆朵欢
+          part: [
+            { label: '娣诲姞闆朵欢', code: 'parts_add', icon: 'plus', isCommonMethod: false },
+            { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
+            // { label: '鍒涘缓宸ュ簭', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
+            { label: '缂栬緫闆朵欢淇℃伅', code: 'parts_edit', icon: 'edit', isCommonMethod: false },
+            // {  label: '瀵煎嚭鏂囨。', code: 'parts_export', icon: 'export', isCommonMethod: true },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'parts_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'parts_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'parts_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '鍒犻櫎', code: 'parts_delete', icon: 'delete', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+          ],
+          //宸ヨ壓瑙勭▼鐗堟湰
+          processSpecVersion: [
+            { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'version_add', icon: 'plus', isCommonMethod: false },
+            { label: '鍒涘缓宸ュ簭', code: 'version_add_child', icon: 'plus', isCommonMethod: false },
+            { label: '缂栬緫宸ヨ壓瑙勭▼鐗堟湰淇℃伅', code: 'version_edit', icon: 'edit', isCommonMethod: false },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'version_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'version_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'version_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '鍒犻櫎', code: 'version_delete', icon: 'delete', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+          ],
+          //宸ュ簭
+          process: [
+            { label: '鍒涘缓宸ュ簭', code: 'process_add', icon: 'plus', isCommonMethod: false },
+            { label: '鍒涘缓宸ユ', code: 'process_add_child', icon: 'plus', isCommonMethod: false },
+            { label: '娣诲姞璁惧绫�', code: 'process_add_type', icon: 'plus', isCommonMethod: true },
+            { label: '缂栬緫宸ュ簭淇℃伅', code: 'process_edit', icon: 'edit', isCommonMethod: false },
+            { label: '鍒犻櫎', code: 'process_delete', icon: 'delete', isCommonMethod: true },
+            // {  label: '瀵煎嚭NC绋嬪簭', code: 'process_export', icon: 'export', isCommonMethod: true },
+            { label: '瀵煎叆NC绋嬪簭', code: 'process_nc_import', icon: 'import', isCommonMethod: true },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'process_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'process_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'process_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true },
+            { label: '鍙戦�乶c绋嬪簭鑷充笁缁村伐鑹�', code: 'process_send_nc_to_plm', icon: 'import', isCommonMethod: true }
+          ],
+          //宸ユ
+          processStep: [
+            { label: '鍒涘缓宸ユ', code: 'processStep_add', icon: 'plus', isCommonMethod: false },
+            { label: '缂栬緫宸ユ淇℃伅', code: 'processStep_edit', icon: 'edit', isCommonMethod: false },
+            { label: '娣诲姞璁惧绫�', code: 'processStep_add_type', icon: 'plus', isCommonMethod: true },
+            { label: '鍒犻櫎', code: 'processStep_delete', icon: 'delete', isCommonMethod: true },
+            // {  label: '瀵煎嚭NC绋嬪簭', code: 'processStep_export', icon: 'import', isCommonMethod: true },
+            { label: '瀵煎叆NC绋嬪簭', code: 'processStep_nc_import', icon: 'import', isCommonMethod: true },
+            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'processStep_other_import', icon: 'import', isCommonMethod: true },
+            { label: '妫�绱㈢數瀛愭牱鏉�', code: 'processStep_search', icon: 'search', isCommonMethod: true },
+            { label: '妫�绱C鏂囦欢', code: 'processStep_search_nc', icon: 'search', isCommonMethod: true },
+            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true },
+            { label: '鍙戦�乶c绋嬪簭鑷充笁缁村伐鑹�', code: 'processStep_send_nc_to_plm', icon: 'import', isCommonMethod: true }
+          ]
+        }
       }
-      console.log('methodName', methodName)
-      const modalTitle = this.defaultContextMenuList[level].find(item => item.code === menuKey).label
-      this.$bus.$emit('treeMenuItemMethodTrigger', { methodName, modalTitle, treeNodeInfo })
+    },
+    computed: {
+      getCurrentMenuLevel() {
+        switch (+this.treeParams.type) {
+          case 1:
+            return 'product'
+          case 2:
+            return 'component'
+          case 3:
+            return 'part'
+          case 4:
+            return 'processSpecVersion'
+          case 5:
+            return 'process'
+          case 6:
+            return 'processStep'
+        }
+      }
+    },
+    methods: {
+      menuItemClick({ key }) {
+        const menuKey = key
+        const level = this.getCurrentMenuLevel
+        const treeNodeInfo = Object.assign({ treeKey: this.treeParams.key }, this.treeParams)
+        console.log('treeNodeInfo', treeNodeInfo)
+        const menuKeyArray = menuKey.split('_')
+        const isCommonMethod = this.defaultContextMenuList[level].find(item => item.code === menuKey).isCommonMethod
+        // product_add => handleAdd 瑙﹀彂瀵瑰簲缁勪欢浜嬩欢
+        let methodName
+        // 鍒ゆ柇鏄惁涓哄叕鍏辨柟娉曪紝濡傛灉涓哄叕鍏辨柟娉曞垯鎴彇涓撴湁灞炴�roduct/component/part/process绛夊瓧娈�
+        if (isCommonMethod) {
+          methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).slice(1).join('')
+        } else {
+          methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).join('')
+        }
+        const modalTitle = this.defaultContextMenuList[level].find(item => item.code === menuKey).label
+        this.$bus.$emit('treeMenuItemMethodTrigger', { methodName, modalTitle, treeNodeInfo })
+      },
+
+      /**
+       * 閬垮厤鍗曟閲嶅鍙抽敭鍚庡叧闂彍鍗曟垨鎵撳紑window鑿滃崟
+       * @param event 浜嬩欢瀵硅薄
+       */
+      menuContextMenu(event) {
+        event.preventDefault()
+        event.stopPropagation()
+      }
     }
   }
-}
 </script>
 
 <style scoped>
-.replaceSearch {
-  color: #40a9ff;
-  font-weight: bold;
-  background-color: rgb(204, 204, 204);
-}
+  /deep/ .ant-menu-item {
+    height: 32px;
+    line-height: 32px;
+  }
 
 </style>
\ No newline at end of file
diff --git a/src/views/dnc/common/TableContextMenu.vue b/src/views/dnc/common/TableContextMenu.vue
index b6bda8c..a72765f 100644
--- a/src/views/dnc/common/TableContextMenu.vue
+++ b/src/views/dnc/common/TableContextMenu.vue
@@ -1,5 +1,5 @@
 <template>
-  <a-menu :style="menuStyle" @click="menuItemClick" v-if="menuVisible" mode="vertical">
+  <a-menu :style="menuStyle" @click="menuItemClick" v-if="menuVisible" mode="vertical" @contextmenu="menuContextMenu">
     <template v-for="menuItem in defaultContextMenuList[tableRowInfo.param]">
       <a-menu-item :key="menuItem.code" v-has="menuItem.code" v-if="menuItem.subMenu.length===0">
         <a-icon :type="menuItem.icon"/>
@@ -168,6 +168,15 @@
           modalTitle,
           tableRowInfo: this.tableRowInfo
         })
+      },
+
+      /**
+       * 閬垮厤鍗曟閲嶅鍙抽敭鍚庡叧闂彍鍗曟垨鎵撳紑window鑿滃崟
+       * @param event 浜嬩欢瀵硅薄
+       */
+      menuContextMenu(event) {
+        event.preventDefault()
+        event.stopPropagation()
       }
     }
   }

--
Gitblit v1.9.3