vue3实现用户角色及权限分配功能(一)
vue3实现用户角色及权限分配功能-- 详细讲解
此为demo练习项目功能记录(个人思路二次整理),如有不恰当之处欢迎指正~
文章目录
1. UserManage及RoleDialog组件生成 : (动态渲染列表页面,设置RoleDialog的显示隐藏状态)
1. RoleList.vue及PermissionDialog 组件生成 : (动态渲染列表页面,设置PermissionDialog 的显示隐藏状态)
一、实现用户角色及权限分配功能的整体思路
- 设置管理员角色(具有角色权限分配的账户 super-admin,初始化配置)
- 设置对应的角色列表,为用户指定角色;
- 设置权限列表. 查看,设置 角色能够查看的权限;
二、实现步骤
(一). 员工管理列表--员工角色分配功能实现
1. UserManage及RoleDialog组件生成 : (动态渲染列表页面,设置RoleDialog的显示隐藏状态)
- 新建 user-manage/UserManage.vue 文件 :根据后端返回的数据,动态渲染用户列表;
- 点击角色,展示dialog:
- 新建 user-manage/modules/RoleDialog.vue
- 调后端接口获取所有的角色信息 , 动态渲染 <el-checkbox / > 组件
<el-checkbox-group v-model="checkList"> <el-checkbox :label="item.title" v-for="item in allRoleList" :key="item.id" /> </el-checkbox-group> // js : const allRoleList = ref([]) // 获取所有的角色信息 const getRoleList = async () => { allRoleList.value = await getRoleListAction() } getRoleList()
- user-manage/UserManage.vue :
- 引入 RoleDialog.vue 组件 并使用
- 新建 const roleDialogVisible = ref (false) --控制dialog的显示隐藏状态
- 为 角色 按钮 绑定 showRoleDialog 方法 , 传入 scope.row(当前行数据--后面获取userId使用) ; 点击时 roleDialogVisibl.value = true ;
const roleVisible = ref(false) const showRoleDialog = (row) => { roleVisible.value = true }
- 使用v-model="roleDialogVisible " 将变量传给 RoleDialog.vue 组件;
<role-dialog v-model="roleVisible" />
- 先插入一段 容易出现的问题,再看下面的完整代码会避免一些小问题:
-
在 3.x 中,自定义组件上的
v-model
相当于传递了modelValue
prop 并接收抛出的update:modelValue
事件:<ChildComponent v-model:title="pageTitle" /> <!-- 是以下的简写: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
// ChildComponent.vue export default { props: { modelValue: String // 以前是`value:String` }, emits: ['update:modelValue'], methods: { changePageTitle(title) { this.$emit('update:modelValue', title) // 以前是 `this.$emit('input', title)` } } }
关于 v-model 的 迁移 -官网地址 :v-model | Vue.js
在user-manage/modules/RoleDialog.vue 修改props的值时 会报以下错误: error Unexpected mutation of "modelValue" prop
-
因为vue默认不允许在子组件修改父组件prop传入的值,使用v-model进行数据绑定时也是对prop进行修改.所以将v-model替换为 :model-value ;
-
-
-
新建 user-manage/modules/RoleDialog.vue :- 使用 defineProps 接收 UserManage.vue 传入的值,v-model绑定传入的值直接使用modelValue进行接收 ;
const props = defineProps({ modelValue: { type: Boolean, required: true } })
- 设置<el-dialog></el-dialog>的:model-value="modelValue" :
- 绑定<el-dialog></el-dialog>的关闭,确认事件 :
- 需要通过 defineEmits 去改变 父组件通过 props 传入的值 ;
- const emits = defineEmits(['update:modelValue'])
- 声明 handleClose 方法 , 调用emits,修改modelValue的值;
const emits = defineEmits(['update:modelValue', 'updateRole']) const handleColse = () => { emits('update:modelValue', false) }
- 使用 defineProps 接收 UserManage.vue 传入的值,v-model绑定传入的值直接使用modelValue进行接收 ;
2. 分配角色:(渲染当前用户已有角色,重新分配角色)
- user-manage/UserManage.vue :
- 通过1.3.3步骤写的方法 showRoleDialog 获取当前点击的 userId , 传入 RoleDialog.vue
const userId = ref('') const showRoleDialog = (row) => { roleVisible.value = true userId.value = row._id }
<role-dialog v-model="roleVisible" :userId="userId" />
- 通过1.3.3步骤写的方法 showRoleDialog 获取当前点击的 userId , 传入 RoleDialog.vue
- user-manage/modules/RoleDialog.vue :
- 通过 props 获取 传参 , 并声明 getCurrentRole 方法获取 当前用户已存在的角色
const props = defineProps({ modelValue: { type: Boolean, required: true }, userId: { type: String } })
const checkList = ref([]) // el-checkbox 双向绑定的参数 const getCurrentRole = async () => { const { role } = await getCurrentRoleAction(props.userId) checkList.value = role.map((item) => item.title) }
- 点击 确定 按钮 触发 handleConfirm 事件 :
- 获取 选中的 角色 数据, 调后端接口,成功后显示成功信息 ;
- 这时更改角色完成,页面不会立即更新,需要手动刷新,角色信息展示才会更新;所以需要 使用 emits 绑定 updateRole 事件 触发页面更新
const emits = defineEmits(['update:modelValue', 'updateRole']) const handleConfirm = async () => { handleColse() const roles = checkList.value.map((title) => { return allRoleList.value.find((item) => item.title === title) }) // 处理成后端需要的数据格式 await updateRoleAction(props.userId, roles) //调用接口 ElMessage.success(i18n.t('msg.role.updateRoleSuccess')) //成功提示 emits('updateRole') // 触发父组件的更新事件 }
- 通过 props 获取 传参 , 并声明 getCurrentRole 方法获取 当前用户已存在的角色
- user-manage/UserManage.vue :
- 接收 子组件传递的 事件 :
<role-dialog v-model="roleVisible" :userId="userId" @updateRole="getTabelData" // getTabelData 列表渲染函数 />
- 接收 子组件传递的 事件 :
以上 : 用户角色功能已实现完成 ;
附完整代码及完整效果展示 :
效果展示 :
user-manage/UserManage.vue :
<template>
<div class="user-manage-container">
<el-card class="user-manage-list">
<el-table
:data="tableData"
style="width: 100%"
border
@select="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column type="index" label="序号" width="80" />
<el-table-column
prop="username"
:label="$t('msg.excel.name')"
width="180"
/>
<el-table-column prop="mobile" :label="$t('msg.excel.mobile')" />
<el-table-column :label="$t('msg.excel.avatar')">
<template #default="scope">
<div class="avatar">
<el-avatar :src="scope.row.avatar"></el-avatar>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('msg.excel.role')" width="180">
<template #default="scope">
<div v-if="scope.row.role && scope.row.role.length > 0">
<el-tag v-for="item in scope.row.role" :key="item.id">{{
item.title
}}</el-tag>
</div>
<div v-else>
<el-tag>{{ $t('msg.excel.defaultRole') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="openTime" :label="$t('msg.excel.openTime')">
<template #default="scope">
{{ $filters.filterDate(scope.row.openTime) }}
</template>
</el-table-column>
<el-table-column :label="$t('msg.excel.action')" width="200">
<template #default="scope">
<div class="flex-justify-center">
<el-button
size="small"
@click="handleDetail(scope.$index, scope.row._id)"
>
{{ $t('msg.excel.show') }}
</el-button>
<el-button
size="small"
type="danger"
@click="showRoleDialog(scope.row)"
>
{{ $t('msg.excel.showRole') }}
</el-button>
<el-popconfirm
:confirm-button-text="$t('msg.universal.confirm')"
:cancel-button-text="$t('msg.universal.cancel')"
:icon="InfoFilled"
icon-color="red"
:title="$t('msg.excel.dialogTitle1')"
@confirm="handleDelete(scope.row._id)"
>
<template #reference>
<el-button size="small" type="danger">
{{ $t('msg.excel.remove') }}
</el-button>
</template>
</el-popconfirm>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
:currentPage="currentPage"
:page-size="pageSize"
:page-sizes="[1, 5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper"
:total="pageTotal"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
class="pagination"
>
</el-pagination>
</el-card>
<export-excel v-model="exportVisible" />
<role-dialog
v-model="roleVisible"
:userId="userId"
@updateRole="getTabelData"
/>
</div>
</template>
<script setup>
import { ref, onActivated} from 'vue'
import { userManageListAction, deleteUserAction } from '@/apis/user'
import { InfoFilled } from '@element-plus/icons-vue'
import RoleDialog from './modules/RoleDialog'
// 表格数据
const tableData = ref([])
// 分页器数据
const currentPage = ref(1)
const pageSize = ref(10)
const pageTotal = ref(0)
// 调用接口 -> 获取table数据
const getTabelData = async () => {
const res = await userManageListAction({
page: currentPage.value,
size: pageSize.value
})
const { list, total } = res
pageTotal.value = total
tableData.value = list
}
getTabelData()
// 激活生命周期
onActivated(getTabelData)
// 分页器功能
const handleSizeChange = (val) => {
pageSize.value = val
getTabelData()
}
const handleCurrentChange = (val) => {
currentPage.value = val
getTabelData()
}
// 分配角色
const roleVisible = ref(false)
const userId = ref('')
const showRoleDialog = (row) => {
roleVisible.value = true
userId.value = row._id
}
</script>
<style lang="scss" scoped></style>
user-manage/modules/RoleDialog.vue :
<template>
<el-dialog
:model-value="modelValue"
:title="$t('msg.excel.roleDialogTitle')"
width="60%"
@close="handleColse"
>
<el-checkbox-group v-model="checkList">
<el-checkbox
:label="item.title"
v-for="item in allRoleList"
:key="item.id"
/>
</el-checkbox-group>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleColse">{{
$t('msg.universal.cancel')
}}</el-button>
<el-button type="primary" @click="handleConfirm">
{{ $t('msg.universal.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue'
import {
getRoleListAction,
getCurrentRoleAction,
updateRoleAction
} from '@/apis/user'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
userId: {
type: String,
required: true
}
})
const emits = defineEmits(['update:modelValue', 'updateRole'])
const handleColse = () => {
emits('update:modelValue', false)
}
const i18n = useI18n()
const handleConfirm = async () => {
handleColse()
const roles = checkList.value.map((title) => {
return allRoleList.value.find((item) => item.title === title)
})
await updateRoleAction(props.userId, roles)
ElMessage.success(i18n.t('msg.role.updateRoleSuccess'))
emits('updateRole')
}
const checkList = ref([])
const allRoleList = ref([])
// 获取所有的角色信息
const getRoleList = async () => {
allRoleList.value = await getRoleListAction()
}
getRoleList()
// 获取当前账户的角色信息
const getCurrentRole = async () => {
const { role } = await getCurrentRoleAction(props.userId)
checkList.value = role.map((item) => item.title)
}
// 监听 当userId变化时 再触发getCurrentRole
watch(
() => props.userId,
(newVal) => {
if (newVal) getCurrentRole()
}
)
</script>
<style lang="scss" scoped></style>
(二). 角色列表 -- 角色权限分配 :
此处功能 与 ( 一 ) 的实现思路及方式基本一致. 就不详细说明了
1. RoleList.vue及PermissionDialog 组件生成 : (动态渲染列表页面,设置PermissionDialog 的显示隐藏状态)
- 新建 RoleList.vue及PermissionDialog 组件 ;
- 初始化样式 .(PermissionDialog --调接口获取所有的权限列表)
- 关于 PermissionDialog 的 显示隐藏设置和 RoleDialog 处理方式一致;
2. 分配权限 :
- 点击 分配权限 按钮 , 获取 roleId 传入 PermissionDialog ;
- PermissionDialog接收 roleId 调后端接口 获取当前角色的权限 ; 动态渲染已有的权限
- 点 确定 按钮 调 后端接口 为当前用户 设置新的权限
实现效果及代码展示 :
RoleList.vue
<template>
<el-card>
<el-table :data="roleList" style="width: 100%" border>
<el-table-column type="index" :label="$t('msg.role.index')" width="80" />
<el-table-column prop="title" :label="$t('msg.role.name')" />
<el-table-column prop="describe" :label="$t('msg.role.desc')" />
<el-table-column :label="$t('msg.role.action')">
<template #default="scope">
<el-button type="primary" @click="showPermissionDialog(scope.row)">
{{ $t('msg.role.assignPermissions') }}
</el-button>
</template>
</el-table-column>
</el-table>
<permission-dialog
v-model="permissionVisible"
:permissionId="permissionId"
/>
</el-card>
</template>
<script setup>
import { ref } from 'vue'
import { getRoleListAction } from '@/apis/user'
import PermissionDialog from './modules/PermissionDialog'
const roleList = ref([])
const getRoleList = async () => {
roleList.value = await getRoleListAction()
}
getRoleList()
const permissionVisible = ref(false)
const permissionId = ref('')
const showPermissionDialog = (row) => {
permissionVisible.value = true
permissionId.value = row.id
}
</script>
<style lang="scss" scoped></style>
PermissionDialog
<template>
<el-dialog
:model-value="modelValue"
:title="$t('msg.excel.roleDialogTitle')"
width="60%"
@close="handleClose"
>
<el-tree
ref="permissionTree"
:data="allPermission"
show-checkbox
node-key="id"
default-expand-all
:props="defaultProps"
/>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">{{
$t('msg.universal.cancel')
}}</el-button>
<el-button type="primary" @click="handleConfirm">
{{ $t('msg.universal.confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue'
import {
getUserPermissionAction,
currentPermissionAction,
updatePermissionAction
} from '@/apis/user'
import { watchSwitchLang } from '@/utils/i18n'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
permissionId: {
type: String,
required: true
}
})
const defaultProps = {
children: 'children',
label: 'permissionName'
}
const allPermission = ref([])
const getAllPermission = async () => {
allPermission.value = await getUserPermissionAction()
}
getAllPermission()
watchSwitchLang(getAllPermission)
// 获取当前用户的权限
const permissionTree = ref(null)
const getCurrentPermission = async () => {
const checkList = await currentPermissionAction(props.permissionId)
permissionTree.value.setCheckedKeys(checkList)
}
watch(
() => props.permissionId,
(newVal) => {
if (newVal) getCurrentPermission()
}
)
const emits = defineEmits(['update:modelValue'])
const handleClose = () => {
emits('update:modelValue', false)
}
const i18n = useI18n()
const handleConfirm = async () => {
handleClose()
const updatePermission = permissionTree.value.getCheckedKeys()
await updatePermissionAction(props.permissionId, updatePermission)
ElMessage.success(i18n.t('msg.role.updateRoleSuccess'))
}
</script>
<style lang="scss" scoped></style>
总结
截止此处,已经实现了项目中对用户角色和权限的分配,但是页面的呈现上还未设置根据权限进行展示. 后面会单独再写一篇文章进行记录.以上如有不当欢迎指正.
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)