zzw
昨天 0642bc9c44d1c8277ada33059c4a3e6156335b30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<template>
  <div class="page">
    <div class="page-title">
      <div><h2>菜单权限管理</h2><p>按角色设置菜单和按钮权限,保存后切换角色会自动反显。</p></div>
    </div>
    <div class="toolbar">
      <span>角色</span>
      <el-select v-model="roleId" style="width:260px" @change="loadPermissions"><el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select>
      <div class="spacer"></div>
      <el-tag type="success">已选权限:{{ selectedRoleIds.length }} 个</el-tag>
      <el-button type="primary" :loading="saving" @click="save">保存角色权限</el-button>
    </div>
    <section class="permission-panel">
      <div class="panel-head"><h3>角色菜单及按钮权限</h3><el-checkbox v-model="roleCheckAll" @change="checkAll">全选</el-checkbox></div>
      <el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState"></el-tree>
    </section>
  </div>
</template>
<script>
import api from '@/api'
export default {
  name: 'MenuPermission',
  data() {
    return {
      menus: [],
      flatMenus: [],
      roles: [],
      roleId: null,
      saving: false,
      roleCheckAll: false,
      selectedRoleIds: []
    }
  },
  computed: {
    allMenuIds() { return this.flatMenus.map(item => String(item.id)) }
  },
  created() { this.loadBase() },
  methods: {
    loadBase() {
      Promise.all([api.get('/system/menu/tree'), api.get('/system/menu/list'), api.get('/system/roles')]).then(([tree, flat, roles]) => {
        this.menus = tree.data || []
        this.flatMenus = flat.data || []
        this.roles = roles.data || []
        if (!this.roleId && this.roles.length) this.roleId = this.roles[0].id
        this.$nextTick(this.loadPermissions)
      })
    },
    loadPermissions() {
      if (!this.roleId) {
        this.selectedRoleIds = []
        this.roleCheckAll = false
        if (this.$refs.roleTree) this.$refs.roleTree.setCheckedKeys([])
        return
      }
      api.get('/system/menu-permissions', { params: { roleId: this.roleId } }).then(res => {
        const data = res.data || {}
        this.selectedRoleIds = (data.roleMenuIds || []).map(String)
        this.$nextTick(() => {
          this.$refs.roleTree.setCheckedKeys(this.displayCheckedKeys(this.selectedRoleIds))
          this.syncCheckAllState()
        })
      })
    },
    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)
    },
    displayCheckedKeys(ids) {
      const selected = new Set((ids || []).map(String))
      const children = {}
      const menuIds = new Set(this.flatMenus.map(item => String(item.id)))
      this.flatMenus.forEach(item => {
        const id = String(item.id)
        const pid = item.pid == null ? '0' : String(item.pid)
        if (!children[pid]) children[pid] = []
        children[pid].push(id)
      })
      const result = []
      const collectFullySelected = id => {
        if (!selected.has(id)) return false
        const childIds = children[id] || []
        if (!childIds.length) {
          result.push(id)
          return true
        }
        const allChildrenSelected = childIds.map(collectFullySelected).every(Boolean)
        if (allChildrenSelected) result.push(id)
        return allChildrenSelected
      }
      this.flatMenus.filter(item => item.pid == null || String(item.pid) === '0' || !menuIds.has(String(item.pid))).forEach(item => collectFullySelected(String(item.id)))
      return result
    },
    checkedMenuIds(ref) { return [...new Set([...(ref.getCheckedKeys(false) || []), ...(ref.getHalfCheckedKeys() || [])].map(String))] },
    syncCheckAllState() {
      const ref = this.$refs.roleTree
      if (!ref) return
      this.roleCheckAll = this.checkedMenuIds(ref).length === this.allMenuIds.length
    },
    checkAll() {
      this.$refs.roleTree.setCheckedKeys(this.roleCheckAll ? this.allMenuIds : [])
      this.$nextTick(this.syncCheckAllState)
    },
    save() {
      if (!this.roleId) {
        this.$message.warning('请先选择角色')
        return
      }
      const roleMenuIds = this.checkedMenuIds(this.$refs.roleTree)
      this.saving = true
      api.post('/system/menu-permissions', { roleId: this.roleId, roleMenuIds }).then(() => {
        this.selectedRoleIds = roleMenuIds
        this.$message.success('角色菜单权限已保存')
      }).finally(() => { this.saving = false })
    }
  }
}
</script>
<style scoped>
::v-deep .perm-button-node { color: #b8791f; }
 
.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;
}
</style>