赞
踩
常见的权限配置是树状的也就是这样的
现在要做的是列表状的也就是这样的
这两种展现形式比较
1、树状的展现形式,相对比较简单、只需要使用element-plus或其他组件库,将后端返回的数据处理成树状数据即可,代码简单,但是如果权限模块较多,就很直观
2、相对的列表状的权限配置,权限就相对直观,一目了然,但是相对的,代码数据处理复杂度会翻倍(当然也有可能是因为我用的方法问题)
- <template>
- <Dialog
- v-model="state.visible"
- title="权限配置"
- width="400px"
- :max-height="500"
- @close="handleClose(false)"
- @open="handleOpen"
- >
- <!-- 表单内容 -->
- <!-- default-checked-keys 带有的节点 -->
- <el-tree
- ref="treeRef"
- :data="state.data"
- show-checkbox
- node-key="id"
- default-expand-all
- :default-checked-keys="state.checkKeys"
- :props="defaultProps"
- />
- <template #footer>
- <el-button @click="state.visible = false"> 取消 </el-button>
- <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
- </template>
- </Dialog>
- </template>
-
- <script lang="ts" setup>
- import { ref, reactive, computed } from 'vue'
- import {
- getAllPermission,
- getPermissionUnderRole,
- addPermissionToRole,
- } from '../../../api'
- import { Permission } from '../../../types'
- const defaultProps = {
- children: 'nodeList',
- label: 'name',
- }
-
- const props = defineProps<{
- visible: boolean
- roleId: number
- }>()
- const emits = defineEmits<{
- (e: 'update:visible', data: boolean)
- (e: 'getList')
- }>()
-
- const treeRef = ref(null)
-
- const state = reactive({
- visible: computed({
- get: () => props.visible,
- set(value: boolean) {
- emits('update:visible', value)
- },
- }),
- data: [] as Permission[],
- checkKeys: [],
- })
-
- const handleSubmit = async () => {
- const res = await addPermissionToRole({
- roleId: props.roleId,
- resourceIds: [
- ...treeRef.value.getCheckedKeys(),
- ...treeRef.value.getHalfCheckedKeys(),
- ],
- })
- if (res.code === '00000000') {
- // 触发父组件重新请求列表数据,没有触发, 所说义会触发bug
- console.log('出发父类请求列表方法')
- emits('getList')
- }
- state.visible = false
- }
-
- // 感觉请求数据的时机还是有点问题
- const handleOpen = async () => {
- // 获取当前角色拥有的权限
- const res2 = await getPermissionUnderRole({
- roleId: props.roleId,
- })
- if (res2.code === '00000000') {
- // 转成树状数据
- let tmp = transToTree(res2.data as Permission[], 0)
- let tmpArr = []
- console.log('tmp', tmp)
- tmp.forEach((item) => {
- item.nodeList.forEach((item2) => {
- if (item2.nodeList.length === 0) {
- tmpArr.push(item2.id)
- }
- tmpArr.push(...item2.nodeList.map((item) => item.id))
- })
- })
- console.log('tmpArr', tmpArr)
- // 以上这都拿主要是 保存当前角色拥有的权限id、拿最后一级的
- // 就是没有nodeList数组或者数组长度为0的
- state.checkKeys = tmpArr
- }
- // 这个api是获取所有可选权限
- const res = await getAllPermission({ roleId: props.roleId })
- if (res.code === '00000000') {
- state.data = res.data as Permission[]
- }
- }
-
- // 递归处理数据---这就是主要难点之一
- function transToTree(data: Permission[], id: number) {
- const arr = []
- data.forEach((item) => {
- if (item.parentId === id) {
- const nodeList = transToTree(data, item.id)
- if (nodeList) {
- item.nodeList = nodeList
- }
- arr.push(item)
- }
- })
- return arr
- }
- const handleClose = (val: boolean) => {
- // state.checkKeys = []
- state.visible = val
- }
- </script>
-
- <style scoped></style>

- <template>
- <div class="roles-setting">
- <el-breadcrumb>
- <el-breadcrumb-item>{{ state.name }}-权限配置</el-breadcrumb-item>
- </el-breadcrumb>
- <div class="setting-box">
- <div class="header">
- <el-checkbox
- v-model="state2.allCheck.selected"
- :indeterminate="state2.allCheck.indeterminate"
- label="全选"
- />
- </div>
- <div class="content">
- <!-- 每个模块的权限 -->
- <div v-for="item in state.data" :key="item.id" class="permission-table">
- <div class="title">{{ item.name }}</div>
- <div class="table-content">
- <!-- 每一行的权限 -->
- <div v-for="item2 in item.nodeList" :key="item2.id" class="row">
- <div class="three-menu">
- <el-checkbox
- v-model="item2.selected"
- :indeterminate="item2.indeterminate"
- :label="item2.name"
- @change="handleCheckAllChange(item2.id)"
- />
- </div>
- <div class="btn-permission">
- <el-checkbox
- v-for="item3 in item2.nodeList"
- :key="item3.id"
- v-model="item3.selected"
- :label="item3.id"
- @change="handleChange(item3.id)"
- >{{ item3.name }}
- </el-checkbox>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="footer">
- <el-button @click="handleCancel">取消</el-button>
- <el-button type="primary" @click="handleSubmit">确认</el-button>
- </div>
- </div>
- </div>
- </template>
-
- <script setup lang="ts">
- import { reactive, onBeforeMount, computed, watch } from 'vue'
- import { ElMessage } from 'element-plus'
- import { useRoute } from 'vue-router'
- // 引入的接口
- import { getAllPermission, addPermissionToRole } from '../../../api'
- // 数据类型
- import { Permission } from '../../../types'
-
- const route = useRoute()
-
- const state = reactive({
- data: [] as Permission[],
- // 因为这是新页面,但是又不在左侧菜单(一般后台的左侧菜单栏)中的,所以f5刷新的时候会
- // roleId及name置空,所说义需要持久保存一下
- name: route.params.name || window.localStorage.getItem('permission-name'),
- roleId:
- route.params.roleId || window.localStorage.getItem('permission-roleId'),
- resourceIds: [], // 保存所有勾选id值,用于发请求
- checked: false,
- })
-
- // 控制最上面全选按钮状态变更
- const state2 = reactive({
- allCheck: {
- selected: computed({
- get() {
- return controlAllCkeckStatus(state.data, 'all')
- },
- set(val: boolean) {
- controlAllCkeckStatus(state.data, 'set', val)
- },
- }),
- indeterminate: computed(() => controlAllCkeckStatus(state.data, 'half')),
- },
- })
-
- // 控制三级菜单的选择状态
- const handleCheckAllChange = (id: number) => {
- // 传id进来,进而控制其nodeList 每项的选择
- const findData = findByIdInTreeData(state.data, id)
- // 去除中间态
- findData.indeterminate = false
- // findData.name = '测试名字'
- // 通过findData 可以直接更改state.data的数据 相当于是将
- // findData = state.data[i].nodeList[j] 浅拷贝会关联数据
- // 循环遍历其下的子级状态
- for (let i = 0; i < findData.nodeList.length; i++) {
- findData.nodeList[i].selected = findData.selected
- }
- }
-
- // 每个按钮权限的状态
- const handleChange = (id: number) => {
- const parentId = findByIdInTreeData(state.data, id).parentId
- const findData = findByIdInTreeData(state.data, parentId)
- // 全选
- if (findData.nodeList.every((item) => item.selected)) {
- findData.selected = true
- findData.indeterminate = false
- } else if (findData.nodeList.some((item) => item.selected)) {
- // 中间态
- findData.selected = false
- findData.indeterminate = true
- } else {
- // 全不选
- findData.selected = false
- findData.indeterminate = false
- }
- }
-
- const getList = async () => {
- // 获取所有可选的权限展示出来
- const res = await getAllPermission({ roleId: state.roleId })
- if (res.code === '00000000') {
- state.data = res.data as Permission[]
- // 循环模块模块(重置权限的状态)--因为后端返回的数据不完全符合我们的需求
- // 第一层--模块数据
- for (let i = 0; i < state.data.length; i++) {
- // 第二层--每行数据
- for (let j = 0; j < state.data[i].nodeList.length; j++) {
- state.data[i].nodeList[j].indeterminate =
- state.data[i].nodeList[j].nodeList.some((item) => item.selected) &&
- !state.data[i].nodeList[j].nodeList.every((item) => item.selected)
- if (state.data[i].nodeList[j].indeterminate) {
- state.data[i].nodeList[j].selected = false
- }
- }
- }
- }
- }
-
- onBeforeMount(async () => {
- // 有值就保存到本地, 避免刷新的时候数据丢失
- if (route.params.roleId && route.params.name) {
- window.localStorage.setItem(
- 'permission-roleId',
- route.params.roleId as string
- )
- window.localStorage.setItem('permission-name', route.params.name as string)
- }
- await getList()
- })
-
- // 取消就重新请求原来的数据
- const handleCancel = async () => {
- await getList()
- }
-
- const handleSubmit = async () => {
- // 将selected为true或indeterminate为true的id取出来放到一起
- getCheckIdFromTreeData(state.data, state.resourceIds)
- const res = await addPermissionToRole({
- roleId: state.roleId,
- resourceIds: state.resourceIds,
- })
- if (res.code === '00000000') {
- ElMessage.success('给角色配置权限成功')
- }
- }
-
- // 根据id查找树状数据里面的某一项
- function findByIdInTreeData(treeData: Permission[], id: number): Permission {
- for (let i = 0; i < treeData.length; i++) {
- if (treeData[i].id === id) {
- return treeData[i]
- } else {
- const findData = findByIdInTreeData(treeData[i].nodeList, id)
- // 找到才返回,没找到就继续找
- if (findData) {
- return findData
- }
- }
- }
- }
-
- // 将勾选的数据的id保存到指定数组
- function getCheckIdFromTreeData(
- treeData: Permission[],
- resultBuf: number[]
- ): void {
- treeData.forEach((item) => {
- if (item.selected || item.indeterminate) {
- resultBuf.push(item.id)
- }
- if (item.nodeList.length > 0) {
- getCheckIdFromTreeData(item.nodeList, resultBuf)
- }
- })
- }
-
- // 控制全选按钮的状态(全选、中间态、全不选、)
- // flag: all--判断是否全选、half判断是否是中间态、set设置权限
- function controlAllCkeckStatus(
- treeData: Permission[],
- flag: string,
- val?: boolean
- ): boolean {
- let buf = []
- if (flag === 'half') {
- for (let i = 0; i < treeData.length; i++) {
- buf[i] =
- (treeData[i].nodeList.some((item) => item.selected) &&
- !treeData[i].nodeList.every((item) => item.selected)) ||
- treeData[i].nodeList.some((item) => item.indeterminate)
- }
- console.log('buf', buf)
- return buf.some((item) => item)
- } else if (flag === 'all') {
- for (let i = 0; i < treeData.length; i++) {
- buf[i] = treeData[i].nodeList.every((item) => item.selected)
- }
- return buf.every((item) => item)
- } else {
- for (let i = 0; i < treeData.length; i++) {
- for (let j = 0; j < treeData[i].nodeList.length; j++) {
- treeData[i].nodeList[j].selected = val
- treeData[i].nodeList[j].indeterminate = false
- handleCheckAllChange(treeData[i].nodeList[j].id)
- }
- }
- }
- }
- </script>
- <style scoped lang="scss">
- .roles-setting {
- width: 100%;
- height: 100%;
- .setting-box {
- position: relative;
- height: 100%;
- margin-top: 10px;
- .header {
- display: flex;
- align-items: center;
- height: 40px;
- width: 100%;
- box-sizing: border-box;
- .el-checkbox {
- margin-left: 10px;
- }
- border-bottom: solid 1px #dcdfe6;
- }
-
- .content {
- width: 100%;
- height: calc(100% - 90px);
- box-sizing: border-box;
- padding: 10px;
- .permission-table {
- margin-bottom: 30px;
- .title {
- margin-bottom: 5px;
- }
- .table-content {
- background: #ededf0;
- border: solid 1px #dcdfe6;
- width: 100%;
- .row {
- display: flex;
- // min-height: 40px;
- align-items: center;
- border-bottom: solid 1px #dcdfe6;
- &:last-child {
- border-bottom: none;
- }
- .three-menu {
- min-width: 150px;
- box-sizing: border-box;
- padding-left: 10px;
- border-right: solid 1px #dcdfe6;
- }
- .btn-permission {
- box-sizing: border-box;
- padding-left: 10px;
- .el-checkbox {
- min-width: 120px;
- }
- }
- }
- }
- }
- }
- .footer {
- border-top: solid 1px #dcdfe6;
- height: 50px;
- box-sizing: border-box;
- width: 100%;
- position: absolute;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- }
- }
- </style>

列表状权限配置的数据结构
- "data": [
- {
- "id": 1,
- "parentId": 0,
- "name": "营销管理",
- "selected": true,
- "nodeList": [
- {
- "id": 3,
- "parentId": 1,
- "name": "优惠券管理",
- "selected": true,
- "nodeList": [
- {
- "id": 11,
- "parentId": 3,
- "name": "查看",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 12,
- "parentId": 3,
- "name": "新增优惠券",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 13,
- "parentId": 3,
- "name": "开启优惠券状态",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 14,
- "parentId": 3,
- "name": "停用优惠券状态",
- "selected": true,
- "nodeList": []
- }
- ]
- },
- {
- "id": 4,
- "parentId": 1,
- "name": "发放管理",
- "selected": true,
- "nodeList": [
- {
- "id": 15,
- "parentId": 4,
- "name": "查看",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 16,
- "parentId": 4,
- "name": "新增发放",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 17,
- "parentId": 4,
- "name": "开启发放状态",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 18,
- "parentId": 4,
- "name": "停用发放状态",
- "selected": true,
- "nodeList": []
- }
- ]
- },
- {
- "id": 5,
- "parentId": 1,
- "name": "领取记录",
- "selected": false,
- "nodeList": [
- {
- "id": 19,
- "parentId": 5,
- "name": "查看",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 20,
- "parentId": 5,
- "name": "导出",
- "selected": false,
- "nodeList": []
- }
- ]
- },
- {
- "id": 6,
- "parentId": 1,
- "name": "ATM壁纸",
- "selected": false,
- "nodeList": [
- {
- "id": 21,
- "parentId": 6,
- "name": "查看",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 22,
- "parentId": 6,
- "name": "新增壁纸",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 23,
- "parentId": 6,
- "name": "开启/批量开启",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 24,
- "parentId": 6,
- "name": "停用/批量停用",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 25,
- "parentId": 6,
- "name": "编辑",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 26,
- "parentId": 6,
- "name": "删除",
- "selected": false,
- "nodeList": []
- }
- ]
- }
- ]
- },
- {
- "id": 2,
- "parentId": 0,
- "name": "供应链",
- "selected": true,
- "nodeList": [
- {
- "id": 7,
- "parentId": 2,
- "name": "校区库存",
- "selected": true,
- "nodeList": [
- {
- "id": 27,
- "parentId": 7,
- "name": "查看",
- "selected": true,
- "nodeList": []
- },
- {
- "id": 28,
- "parentId": 7,
- "name": "详情",
- "selected": true,
- "nodeList": []
- }
- ]
- },
- {
- "id": 8,
- "parentId": 2,
- "name": "仓库库存",
- "selected": false,
- "nodeList": [
- {
- "id": 29,
- "parentId": 8,
- "name": "查看",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 30,
- "parentId": 8,
- "name": "详情",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 31,
- "parentId": 8,
- "name": "新增",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 32,
- "parentId": 8,
- "name": "修改",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 33,
- "parentId": 8,
- "name": "删除",
- "selected": false,
- "nodeList": []
- }
- ]
- },
- {
- "id": 9,
- "parentId": 2,
- "name": "打印机库存",
- "selected": false,
- "nodeList": []
- },
- {
- "id": 10,
- "parentId": 2,
- "name": "采购库存",
- "selected": false,
- "nodeList": []
- }
- ]
- }
- ]

个人感觉数据处理还有很多优化的地方,暂时就先这样!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。