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/DeviceStructure/DeviceStructureTree.vue |  538 +++++++++++++++++++++++++++++++----------------------------
 1 files changed, 285 insertions(+), 253 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>

--
Gitblit v1.9.3