From 757dcb3fcad665eaef93074a4981c4134f7a027e Mon Sep 17 00:00:00 2001
From: zzw <228544128@qq.com>
Date: 星期日, 04 十月 2026 17:33:43 +0800
Subject: [PATCH] feat: complete system management and repair workflow updates

---
 emergency-repair-web/src/views/system/MenuPermission.vue |   33 +++++++++++++++++++++++----------
 1 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/emergency-repair-web/src/views/system/MenuPermission.vue b/emergency-repair-web/src/views/system/MenuPermission.vue
index ad5033f..347bd0a 100644
--- a/emergency-repair-web/src/views/system/MenuPermission.vue
+++ b/emergency-repair-web/src/views/system/MenuPermission.vue
@@ -15,11 +15,11 @@
     <div class="permission-grid">
       <section class="permission-panel">
         <div class="panel-head"><h3>绉熸埛鑿滃崟鎺堟潈</h3><el-checkbox v-model="tenantCheckAll" @change="checkAll('tenant')">鍏ㄩ��</el-checkbox></div>
-        <el-tree ref="tenantTree" :data="menus" node-key="id" show-checkbox check-strictly default-expand-all :props="{ label: 'name', children: 'children' }"></el-tree>
+        <el-tree ref="tenantTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState('tenant')"></el-tree>
       </section>
       <section class="permission-panel">
         <div class="panel-head"><h3>瑙掕壊鑿滃崟鎺堟潈</h3><el-checkbox v-model="roleCheckAll" @change="checkAll('role')">鍏ㄩ��</el-checkbox></div>
-        <el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox check-strictly default-expand-all :props="{ label: 'name', children: 'children' }"></el-tree>
+        <el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState('role')"></el-tree>
       </section>
       <section class="permission-panel intersection-panel">
         <div class="panel-head"><h3>浜ら泦棰勮</h3></div>
@@ -34,14 +34,16 @@
   name: 'MenuPermission',
   data() { return { menus: [], flatMenus: [], roles: [], tenants: [], tenantId: 1, roleId: null, saving: false, tenantCheckAll: false, roleCheckAll: false, selectedTenantIds: [], selectedRoleIds: [] } },
   computed: {
-    allMenuIds() { return this.flatMenus.map(item => item.id) },
+    allMenuIds() { return this.flatMenus.map(item => String(item.id)) },
+    parentMenuIds() { const ids = new Set(); this.flatMenus.forEach(item => ids.add(String(item.pid))); return ids },
+    leafMenuIds() { return this.flatMenus.filter(item => !this.parentMenuIds.has(String(item.id))).map(item => String(item.id)) },
     effectiveCount() { return this.effectiveMenus.length },
-    effectiveMenus() { return this.flatMenus.filter(item => this.selectedTenantIds.includes(item.id) && this.selectedRoleIds.includes(item.id)) }
+    effectiveMenus() { return this.flatMenus.filter(item => this.selectedTenantIds.map(String).includes(String(item.id)) && this.selectedRoleIds.map(String).includes(String(item.id))) }
   },
   created() { this.loadBase() },
   methods: {
     loadBase() {
-      Promise.all([api.get('/system/menu/nav'), api.get('/system/menu/list'), api.get('/system/roles'), api.get('/system/tenants')]).then(([tree, flat, roles, tenants]) => {
+      Promise.all([api.get('/system/menu/tree'), api.get('/system/menu/list'), api.get('/system/roles'), api.get('/system/tenants')]).then(([tree, flat, roles, tenants]) => {
         this.menus = tree.data || []; this.flatMenus = flat.data || []; this.roles = roles.data || []; this.tenants = tenants.data || []
         if (!this.roleId && this.roles.length) this.roleId = this.roles[0].id
         this.$nextTick(this.loadPermissions)
@@ -54,19 +56,28 @@
         this.selectedTenantIds = data.tenantMenuIds || []
         this.selectedRoleIds = data.roleMenuIds || []
         this.$nextTick(() => {
-          this.$refs.tenantTree.setCheckedKeys(this.selectedTenantIds)
-          this.$refs.roleTree.setCheckedKeys(this.selectedRoleIds)
+          this.$refs.tenantTree.setCheckedKeys(this.leafKeys(this.selectedTenantIds))
+          this.$refs.roleTree.setCheckedKeys(this.leafKeys(this.selectedRoleIds))
+          this.syncCheckAllState('tenant'); this.syncCheckAllState('role')
         })
       })
     },
+    renderContent(h, { data }) {
+      const label = data.menuType === 2 ? data.name + ' [鎸夐挳 ' + (data.permissions || '') + ']' : data.name
+      return h('span', { class: { 'perm-button-node': data.menuType === 2 } }, label)
+    },
+    leafKeys(ids) { const leafIds = new Set(this.leafMenuIds); return (ids || []).map(String).filter(id => leafIds.has(id)) },
+    checkedMenuIds(ref) { return [...new Set([...(ref.getCheckedKeys(false) || []), ...(ref.getHalfCheckedKeys() || [])].map(String))] },
+    syncCheckAllState(target) { const ref = target === 'tenant' ? this.$refs.tenantTree : this.$refs.roleTree; if (!ref) return; const checked = this.checkedMenuIds(ref).length === this.allMenuIds.length; if (target === 'tenant') this.tenantCheckAll = checked; if (target === 'role') this.roleCheckAll = checked },
     checkAll(target) {
       const checked = target === 'tenant' ? this.tenantCheckAll : this.roleCheckAll
       const ref = target === 'tenant' ? this.$refs.tenantTree : this.$refs.roleTree
       ref.setCheckedKeys(checked ? this.allMenuIds : [])
+      this.$nextTick(() => this.syncCheckAllState(target))
     },
     save() {
-      const tenantMenuIds = this.$refs.tenantTree.getCheckedKeys()
-      const roleMenuIds = this.$refs.roleTree.getCheckedKeys()
+      const tenantMenuIds = this.checkedMenuIds(this.$refs.tenantTree)
+      const roleMenuIds = this.checkedMenuIds(this.$refs.roleTree)
       this.saving = true
       api.post('/system/menu-permissions', { tenantId: this.tenantId, roleId: this.roleId, tenantMenuIds, roleMenuIds }).then(() => {
         this.selectedTenantIds = tenantMenuIds; this.selectedRoleIds = roleMenuIds; this.$message.success('鑿滃崟鏉冮檺宸蹭繚瀛�')
@@ -76,8 +87,10 @@
 }
 </script>
 <style scoped>
+::v-deep .perm-button-node { color: #b8791f; }
+
 .permission-grid { display: grid; grid-template-columns: 1fr 1fr 320px; gap: 12px; }
-.permission-panel { min-height: calc(100vh - 180px); padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; overflow: auto; }
+.permission-panel { min-height: calc(100vh - 220px); padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; overflow: auto; }
 .panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
 .panel-head h3 { margin: 0; color: #31465d; font-size: 15px; }
 .intersection-list { display: flex; flex-wrap: wrap; gap: 6px; }

--
Gitblit v1.9.1