Przeglądaj źródła

update:admin 租户管理菜单优化

ct 1 dzień temu
rodzic
commit
62f065a042
1 zmienionych plików z 106 dodań i 51 usunięć
  1. 106 51
      src/views/admin/sysCompany/index.vue

+ 106 - 51
src/views/admin/sysCompany/index.vue

@@ -245,17 +245,20 @@
     </el-dialog>
 
     <!-- ===== 菜单编辑弹窗 ===== -->
-    <el-dialog :title="menuDialog.title" :visible.sync="menuDialog.visible" width="560px" append-to-body destroy-on-close>
-      <div v-loading="menuDialog.loading" style="min-height:100px">
+    <el-dialog :title="menuDialog.title" :visible.sync="menuDialog.visible" width="560px" append-to-body destroy-on-close @opened="onMenuDialogOpened">
+      <div v-loading="menuDialog.loading" class="menu-tree-scroll">
+        <div v-if="menuDialog.treeReady && menuDialog.treeData.length" class="menu-tree-toolbar">
+          <el-button type="text" size="mini" @click="expandAllMenuNodes">展开全部</el-button>
+          <el-button type="text" size="mini" @click="collapseAllMenuNodes">收起全部</el-button>
+        </div>
         <el-tree
-          v-if="menuDialog.treeData.length"
+          v-if="menuDialog.treeReady"
           ref="menuTree"
           :data="menuDialog.treeData"
           show-checkbox
           node-key="id"
-          :default-checked-keys="menuDialog.checkedKeys"
-          :props="{ label: 'label', children: 'children' }"
-          default-expand-all
+          :props="menuTreeProps"
+          :expand-on-click-node="false"
         >
           <span slot-scope="{ data }" class="custom-tree-node">
             <span>{{ data.label }}</span>
@@ -263,7 +266,7 @@
             <el-tag v-else type="info" size="mini" style="margin-left:8px">隐藏</el-tag>
           </span>
         </el-tree>
-        <el-empty v-if="!menuDialog.loading && !menuDialog.treeData.length" description="暂无菜单数据" />
+        <el-empty v-if="!menuDialog.loading && menuDialog.treeReady && !menuDialog.treeData.length" description="暂无菜单数据" />
       </div>
       <div slot="footer">
         <el-button @click="menuDialog.visible = false">取 消</el-button>
@@ -376,16 +379,18 @@ export default {
         operateType: [{ required: true, message: '请选择操作类型', trigger: 'change' }],
         amount: [{ required: true, message: '请输入金额', trigger: 'blur' }]
       },
+      menuTreeProps: { label: 'label', children: 'children' },
       // 菜单编辑弹窗
       menuDialog: {
         visible: false,
         title: '',
-        flag: 'sys',       // sys=管理端菜单, com=销售菜单
+        flag: 'sys',
         companyId: null,
         companyName: '',
         treeData: [],
-        checkedKeys: [],    // 初始勾选的菜单ID(visible=0)
-        allMenuIds: [],     // 所有菜单ID
+        treeReady: false,
+        checkedKeys: [],
+        checkedKeySet: null,
         loading: false,
         submitting: false
       },
@@ -583,6 +588,76 @@ export default {
         this.exportLoading = false
       }).catch(() => { this.exportLoading = false })
     },
+    collectCheckedFromTree(nodes) {
+      const ids = []
+      const walk = (list) => {
+        (list || []).forEach(n => {
+          if (n.visible === '0') ids.push(n.id)
+          if (n.children && n.children.length) walk(n.children)
+        })
+      }
+      walk(nodes)
+      return ids
+    },
+    applyMenuTreeChecked(menus) {
+      const tree = this.$refs.menuTree
+      const d = this.menuDialog
+      if (!tree) return
+      tree.setCheckedKeys(this.collectCheckedFromTree(menus))
+      const keys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys())
+      d.checkedKeys = keys
+      d.checkedKeySet = new Set(keys)
+    },
+    walkMenuTreeNodes(nodes, fn) {
+      (nodes || []).forEach(n => {
+        fn(n)
+        if (n.children && n.children.length) this.walkMenuTreeNodes(n.children, fn)
+      })
+    },
+    expandAllMenuNodes() {
+      const tree = this.$refs.menuTree
+      if (!tree) return
+      this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
+        const node = tree.getNode(n.id)
+        if (node) node.expanded = true
+      })
+    },
+    collapseAllMenuNodes() {
+      const tree = this.$refs.menuTree
+      if (!tree) return
+      this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
+        const node = tree.getNode(n.id)
+        if (node) node.expanded = false
+      })
+    },
+    onMenuDialogOpened() {
+      if (this.menuDialog.companyId) {
+        this.loadTenantMenuTree()
+      }
+    },
+    loadTenantMenuTree() {
+      const d = this.menuDialog
+      if (!d.companyId) return
+      d.loading = true
+      d.treeReady = false
+      d.treeData = []
+      getTenantMenuTree(d.companyId, d.flag).then(res => {
+        if (res.code !== 200) {
+          return Promise.reject(new Error(res.msg || '加载菜单失败'))
+        }
+        const menus = res.menus || []
+        // 延迟挂载树,先出弹窗再渲染,默认全部收起
+        d.treeData = menus
+        requestAnimationFrame(() => {
+          d.treeReady = true
+          this.$nextTick(() => this.applyMenuTreeChecked(menus))
+        })
+      }).catch(err => {
+        this.$message.error(err.message || '加载菜单失败')
+      }).finally(() => {
+        d.loading = false
+      })
+    },
     /** 编辑菜单(管理端/销售端) */
     handleEditMenu(row, flag) {
       this.menuDialog = {
@@ -592,44 +667,12 @@ export default {
         companyId: row.id,
         companyName: row.tenantName,
         treeData: [],
+        treeReady: false,
         checkedKeys: [],
-        allMenuIds: [],
-        loading: true,
+        checkedKeySet: null,
+        loading: false,
         submitting: false
       }
-      getTenantMenuTree(row.id, flag).then(res => {
-        if (res.code === 200) {
-          const menus = res.menus || []
-          this.menuDialog.treeData = menus
-          // 递归提取所有菜单ID + visible=0的ID(勾选状态)
-          const allIds = []
-          const checkedIds = []
-          const walk = (nodes) => {
-            if (!nodes) return
-            nodes.forEach(n => {
-              allIds.push(n.id)
-              if (n.visible === '0') checkedIds.push(n.id)
-              if (n.children && n.children.length) walk(n.children)
-            })
-          }
-          walk(menus)
-          this.menuDialog.checkedKeys = checkedIds
-          this.menuDialog.allMenuIds = allIds
-          // 等tree渲染后捕获级联展开的实际勾选状态作为基准
-          this.$nextTick(() => {
-            const tree = this.$refs.menuTree
-            if (tree) {
-              this.menuDialog.checkedKeys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys())
-            }
-          })
-        } else {
-          this.$message.error(res.msg || '加载菜单失败')
-        }
-      }).catch(() => {
-        this.$message.error('加载菜单失败')
-      }).finally(() => {
-        this.menuDialog.loading = false
-      })
     },
     /** 模块定价 */
     handleModulePricing(row) {
@@ -704,11 +747,10 @@ export default {
       if (!tree) return
       // 级联模式:纳入全选+半选节点作为当前勾选状态
       const currentChecked = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys())
-      const originalChecked = this.menuDialog.checkedKeys
-      // selected = 新勾选的(当前有,原来没有)
-      const selected = currentChecked.filter(id => !originalChecked.includes(id))
-      // unSelected = 新取消的(原来有,当前没有)
-      const unSelected = originalChecked.filter(id => !currentChecked.includes(id))
+      const currentSet = new Set(currentChecked)
+      const originalSet = this.menuDialog.checkedKeySet || new Set(this.menuDialog.checkedKeys)
+      const selected = currentChecked.filter(id => !originalSet.has(id))
+      const unSelected = this.menuDialog.checkedKeys.filter(id => !currentSet.has(id))
       if (selected.length === 0 && unSelected.length === 0) {
         this.$message.info('菜单无变化')
         this.menuDialog.visible = false
@@ -740,5 +782,18 @@ export default {
 .mb8 { margin-bottom: 8px; }
 .mb16 { margin-bottom: 16px; }
 .filter-card { padding-bottom: 0; }
-.custom-tree-node { display: flex; align-items: center; font-size: 13px; }
+.menu-tree-scroll {
+  min-height: 120px;
+  max-height: 420px;
+  overflow-y: auto;
+}
+.menu-tree-toolbar {
+  margin-bottom: 8px;
+  text-align: right;
+}
+.custom-tree-node {
+  display: flex;
+  align-items: center;
+  font-size: 13px;
+}
 </style>