فهرست منبع

1、调整菜单问题

yys 1 هفته پیش
والد
کامیت
6ecfce6bcd
2فایلهای تغییر یافته به همراه60 افزوده شده و 32 حذف شده
  1. 1 1
      src/api/admin/sysCompany.js
  2. 59 31
      src/views/admin/sysCompany/index.vue

+ 1 - 1
src/api/admin/sysCompany.js

@@ -106,7 +106,7 @@ export function getTenantMenuTree(companyId, flag) {
   })
   })
 }
 }
 
 
-// 编辑租户菜单(勾选/取消
+// 编辑租户菜单(提交当前完整勾选 menuId 列表,后端全量替换
 export function editTenantMenu(companyId, data) {
 export function editTenantMenu(companyId, data) {
   return request({
   return request({
     url: '/admin/company/' + companyId + '/menu/edit',
     url: '/admin/company/' + companyId + '/menu/edit',

+ 59 - 31
src/views/admin/sysCompany/index.vue

@@ -290,7 +290,7 @@
             <el-tag v-if="data.menuType === 'M'" size="mini" style="margin-left:6px">目录</el-tag>
             <el-tag v-if="data.menuType === 'M'" size="mini" style="margin-left:6px">目录</el-tag>
             <el-tag v-else-if="data.menuType === 'C'" type="success" size="mini" style="margin-left:6px">菜单</el-tag>
             <el-tag v-else-if="data.menuType === 'C'" type="success" size="mini" style="margin-left:6px">菜单</el-tag>
             <el-tag v-else-if="data.menuType === 'F'" type="warning" size="mini" style="margin-left:6px">按钮</el-tag>
             <el-tag v-else-if="data.menuType === 'F'" type="warning" size="mini" style="margin-left:6px">按钮</el-tag>
-            <el-tag v-if="data.visible === '0'" type="success" size="mini" style="margin-left:6px">已分配</el-tag>
+            <el-tag v-if="isMenuAssigned(data)" type="success" size="mini" style="margin-left:6px">已分配</el-tag>
             <el-tag v-else type="info" size="mini" style="margin-left:6px">未分配</el-tag>
             <el-tag v-else type="info" size="mini" style="margin-left:6px">未分配</el-tag>
           </span>
           </span>
         </el-tree>
         </el-tree>
@@ -424,6 +424,7 @@ export default {
         treeData: [],
         treeData: [],
         treeReady: false,
         treeReady: false,
         checkedKeys: [],
         checkedKeys: [],
+        checkedKeysFromApi: [],
         checkedKeySet: null,
         checkedKeySet: null,
         initialAssignedIds: null,
         initialAssignedIds: null,
         loading: false,
         loading: false,
@@ -638,11 +639,27 @@ export default {
       const n = Number(id)
       const n = Number(id)
       return Number.isNaN(n) ? id : n
       return Number.isNaN(n) ? id : n
     },
     },
+    /** 租户库已分配:优先 checkedKeys,其次 visible=0(不再依赖 status,避免按钮类菜单回显失败) */
+    isMenuAssigned(menu) {
+      if (!menu) return false
+      const menuId = this.normalizeMenuId(menu.menuId)
+      if (menuId != null && this.menuDialog.checkedKeySet && this.menuDialog.checkedKeySet.has(menuId)) {
+        return true
+      }
+      return menu.visible === '0' || menu.visible === 0
+    },
+    normalizeMenuFlatList(menus) {
+      return (menus || []).map(m => ({
+        ...m,
+        menuId: this.normalizeMenuId(m.menuId),
+        parentId: m.parentId != null && m.parentId !== '' ? this.normalizeMenuId(m.parentId) : m.parentId
+      }))
+    },
     collectAssignedMenuIds(nodes) {
     collectAssignedMenuIds(nodes) {
       const ids = []
       const ids = []
       const walk = (list) => {
       const walk = (list) => {
         (list || []).forEach(n => {
         (list || []).forEach(n => {
-          if (n.visible === '0') {
+          if (this.isMenuAssigned(n)) {
             const mid = this.normalizeMenuId(n.menuId)
             const mid = this.normalizeMenuId(n.menuId)
             if (mid != null) ids.push(mid)
             if (mid != null) ids.push(mid)
           }
           }
@@ -652,19 +669,19 @@ export default {
       walk(nodes)
       walk(nodes)
       return ids
       return ids
     },
     },
-    applyMenuTreeChecked(menus) {
+    applyMenuTreeChecked(checkedKeys) {
       const tree = this.$refs.menuTree
       const tree = this.$refs.menuTree
       const d = this.menuDialog
       const d = this.menuDialog
       if (!tree) return
       if (!tree) return
-      // 勿用 setCheckedKeys(父节点):会级联勾选未分配子节点,导致提交 diff 为空
+      const keys = (checkedKeys || []).map(id => this.normalizeMenuId(id)).filter(id => id != null)
       tree.setCheckedKeys([])
       tree.setCheckedKeys([])
-      this.collectAssignedMenuIds(menus).forEach(menuId => {
+      keys.forEach(menuId => {
         const node = tree.getNode(menuId)
         const node = tree.getNode(menuId)
         if (node) node.setChecked(true, false)
         if (node) node.setChecked(true, false)
       })
       })
-      const keys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys()).map(id => this.normalizeMenuId(id))
-      d.checkedKeys = keys
-      d.checkedKeySet = new Set(keys)
+      const currentKeys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys()).map(id => this.normalizeMenuId(id))
+      d.checkedKeys = currentKeys
+      d.checkedKeySet = new Set(currentKeys)
     },
     },
     walkMenuTreeNodes(nodes, fn) {
     walkMenuTreeNodes(nodes, fn) {
       (nodes || []).forEach(n => {
       (nodes || []).forEach(n => {
@@ -680,21 +697,26 @@ export default {
       })
       })
       return ids
       return ids
     },
     },
-    checkAllMenuNodes() {
+    setAllMenuNodesChecked(checked) {
       const tree = this.$refs.menuTree
       const tree = this.$refs.menuTree
       if (!tree) return
       if (!tree) return
-      tree.setCheckedKeys(this.collectAllMenuIds(this.menuDialog.treeData))
+      // 与回显一致:逐节点 setChecked,避免 setCheckedKeys(父节点) 级联导致 diff 异常
+      this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
+        const node = tree.getNode(this.normalizeMenuId(n.menuId))
+        if (node) node.setChecked(checked, false)
+      })
+    },
+    checkAllMenuNodes() {
+      this.setAllMenuNodesChecked(true)
     },
     },
     uncheckAllMenuNodes() {
     uncheckAllMenuNodes() {
-      const tree = this.$refs.menuTree
-      if (!tree) return
-      tree.setCheckedKeys([])
+      this.setAllMenuNodesChecked(false)
     },
     },
     expandAllMenuNodes() {
     expandAllMenuNodes() {
       const tree = this.$refs.menuTree
       const tree = this.$refs.menuTree
       if (!tree) return
       if (!tree) return
       this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
       this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
-        const node = tree.getNode(n.menuId)
+        const node = tree.getNode(this.normalizeMenuId(n.menuId))
         if (node) node.expanded = true
         if (node) node.expanded = true
       })
       })
     },
     },
@@ -702,7 +724,7 @@ export default {
       const tree = this.$refs.menuTree
       const tree = this.$refs.menuTree
       if (!tree) return
       if (!tree) return
       this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
       this.walkMenuTreeNodes(this.menuDialog.treeData, n => {
-        const node = tree.getNode(n.menuId)
+        const node = tree.getNode(this.normalizeMenuId(n.menuId))
         if (node) node.expanded = false
         if (node) node.expanded = false
       })
       })
     },
     },
@@ -721,16 +743,19 @@ export default {
         if (res.code !== 200) {
         if (res.code !== 200) {
           return Promise.reject(new Error(res.msg || '加载菜单失败'))
           return Promise.reject(new Error(res.msg || '加载菜单失败'))
         }
         }
-        const flat = res.menus || []
-        d.initialAssignedIds = new Set(
-          flat.filter(m => m.visible === '0').map(m => this.normalizeMenuId(m.menuId)).filter(id => id != null)
-        )
+        const flat = this.normalizeMenuFlatList(res.menus || [])
+        const checkedFromApi = (res.checkedKeys || [])
+          .map(id => this.normalizeMenuId(id))
+          .filter(id => id != null)
+        d.checkedKeysFromApi = checkedFromApi
+        d.initialAssignedIds = new Set(checkedFromApi)
+        d.checkedKeySet = new Set(checkedFromApi)
         // 与模板维护页相同:扁平数据 + handleTree(menuId) 建树,保证层级一致
         // 与模板维护页相同:扁平数据 + handleTree(menuId) 建树,保证层级一致
         const tree = this.handleTree(flat, 'menuId')
         const tree = this.handleTree(flat, 'menuId')
         d.treeData = tree
         d.treeData = tree
         requestAnimationFrame(() => {
         requestAnimationFrame(() => {
           d.treeReady = true
           d.treeReady = true
-          this.$nextTick(() => this.applyMenuTreeChecked(tree))
+          this.$nextTick(() => this.applyMenuTreeChecked(checkedFromApi))
         })
         })
       }).catch(err => {
       }).catch(err => {
         this.$message.error(err.message || '加载菜单失败')
         this.$message.error(err.message || '加载菜单失败')
@@ -749,6 +774,7 @@ export default {
         treeData: [],
         treeData: [],
         treeReady: false,
         treeReady: false,
         checkedKeys: [],
         checkedKeys: [],
+        checkedKeysFromApi: [],
         checkedKeySet: null,
         checkedKeySet: null,
         initialAssignedIds: null,
         initialAssignedIds: null,
         loading: false,
         loading: false,
@@ -889,28 +915,30 @@ export default {
         this.pricingDialog.submitting = false
         this.pricingDialog.submitting = false
       })
       })
     },
     },
-    /** 提交菜单编辑 */
+    /** 提交菜单编辑:后端为全量替换,须提交当前完整勾选列表 */
     submitMenuEdit() {
     submitMenuEdit() {
       const tree = this.$refs.menuTree
       const tree = this.$refs.menuTree
       if (!tree) return
       if (!tree) return
-      const initialAssigned = this.menuDialog.initialAssignedIds || new Set()
-      const currentSelected = tree.getCheckedKeys()
+      const selected = tree.getCheckedKeys()
         .concat(tree.getHalfCheckedKeys())
         .concat(tree.getHalfCheckedKeys())
         .map(id => this.normalizeMenuId(id))
         .map(id => this.normalizeMenuId(id))
         .filter(id => id != null)
         .filter(id => id != null)
-      const currentSet = new Set(currentSelected)
-      const selected = currentSelected.filter(id => !initialAssigned.has(id))
-      const unSelected = [...initialAssigned].filter(id => !currentSet.has(id))
-      if (selected.length === 0 && unSelected.length === 0) {
-        this.$message.info('菜单无变化')
-        this.menuDialog.visible = false
+      const uniqueSelected = [...new Set(selected)]
+      if (uniqueSelected.length === 0) {
+        this.$confirm('未勾选任何菜单,保存后将清空该租户已分配的菜单,是否继续?', '提示', {
+          type: 'warning'
+        }).then(() => {
+          this.doSubmitMenuEdit(uniqueSelected)
+        }).catch(() => {})
         return
         return
       }
       }
+      this.doSubmitMenuEdit(uniqueSelected)
+    },
+    doSubmitMenuEdit(selected) {
       this.menuDialog.submitting = true
       this.menuDialog.submitting = true
       editTenantMenu(this.menuDialog.companyId, {
       editTenantMenu(this.menuDialog.companyId, {
         flag: this.menuDialog.flag,
         flag: this.menuDialog.flag,
-        selected: selected,
-        unSelected: unSelected
+        selected
       }).then(res => {
       }).then(res => {
         if (res.code === 200) {
         if (res.code === 200) {
           this.$message.success('菜单更新成功')
           this.$message.success('菜单更新成功')