一.后端实现
(1).新建apps/groups包
(2)settings.py中注册-----'groups'
(3)groups/serializers.py:
from django.contrib.auth.models import Group from rest_framework import serializers class GroupSerializer(serializers.ModelSerializer): """ group序列化类 """ class Meta: model = Group fields = ("id", "name")
(4)groups/views.py:
from django.contrib.auth.models import Group from rest_framework import viewsets, mixins from .serializers import GroupSerializer from .filters import GroupFilter class GroupViewset(viewsets.ModelViewSet): queryset = Group.objects.all() serializer_class = GroupSerializer filter_class = GroupFilter filter_fields = ("name",)
(5)groups/filters.py:
import django_filters from django.contrib.auth.models import Group class GroupFilter(django_filters.rest_framework.FilterSet): """ group过滤类 """ name = django_filters.CharFilter(lookup_expr='icontains') class Meta: model = Group fields = ('name',)
(6)groups/router.py:
from rest_framework.routers import DefaultRouter from .views import GroupViewset group_router = DefaultRouter() group_router.register('groups', GroupViewset, basename="groups")
(7)devops/urls.py:
from django.conf.urls import url, include from rest_framework.routers import DefaultRouter from rest_framework.documentation import include_docs_urls from groupUsers .views import GroupUsersViewset from users.router import router as user_router from groups.router import group_router router = DefaultRouter() router.registry.extend(user_router.registry) router.registry.extend(group_router.registry) from rest_framework.authtoken.views import obtain_auth_token urlpatterns = [ url(r'^api/', include(router.urls)), url(r'^', include('resources.urls')), url(r'^api-auth/', include('rest_framework.urls')), url(r'^docs/', include_docs_urls("51reboot接口文档")), url(r'^api-token-auth/', obtain_auth_token) ]
(python36env) [vagrant@CentOS devops]$ python manage.py runserver 0.0.0.0:8000
二.前端实现
1.页面展示
(1).views/groups/index.vue
<template> <div class="app-container"> <el-table :data="groupData" stripe style=" 100%"> <el-table-column type="index" width="50" /> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleModifyGroup(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> </div> </template> <script> import { getGroupList } from '@/api/group' export default { name: 'Groups', data() { return { groupData: [], total: 0, params: { page: 1 } } }, created() { this.fetchGroupList() }, methods: { fetchGroupList() { getGroupList().then(res => { this.groupData = res.results this.total = res.count }) }, handleChange(val) { this.params.page = val this.fetchGroupList() } } } </script>
(2).router/index.js
import Vue from 'vue' import Router from 'vue-router' // in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; // detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading Vue.use(Router) /* Layout */ import Layout from '../views/layout/Layout' /** * hidden: true if `hidden:true` will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu, whatever its child routes length * if not set alwaysShow, only more than one route under the children * it will becomes nested mode, otherwise not show the root menu * redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb * name:'router-name' the name is used by <keep-alive> (must set!!!) * meta : { title: 'title' the name show in submenu and breadcrumb (recommend set) icon: 'svg-name' the icon show in the sidebar, } **/ export const constantRouterMap = [ { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', name: 'Dashboard', children: [{ path: 'dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'dashboard', icon: 'example' } }] }, { path: '/users', component: Layout, name: 'users', meta: { title: '用户管理', icon: 'example' }, children: [ { path: 'user', name: 'user', component: () => import('@/views/users/user'), meta: { title: '用户' } }, { path: 'groups', name: 'groups', component: () => import('@/views/groups'), meta: { title: '用户组' } } ] }, { path: '*', redirect: '/404', hidden: true } ] export default new Router({ mode: 'history', scrollBehavior: () => ({ y: 0 }), routes: constantRouterMap })
(3)api/group.js:
import request from '@/utils/request' export function getGroupList(params) { return request({ url: '/api/groups/', method: 'get', params }) }
效果如下图:
2.添加用户组
(1)groups/index.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.name" placeholder="搜索用户组" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button type="primary" @click="groupFormVisible=true">增加用户组</el-button> </el-col> </el-row> <el-table :data="groupData" stripe style=" 100%"> <el-table-column type="index" width="50" /> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleModifyGroup(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <GroupForm v-model="groupFormVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> </div> </template> <script> import { getGroupList } from '@/api/group' import GroupForm from './components/groupForm' export default { name: 'Groups', components: { GroupForm }, data() { return { groupData: [], total: 0, params: { page: 1, name: '' }, groupFormVisible: false, groupId: 0, groupName: '' } }, created() { this.fetchGroupList() }, methods: { fetchGroupList() { getGroupList(this.params).then(res => { this.groupData = res.results this.total = res.count }) }, handleChange(val) { this.params.page = val this.fetchGroupList() }, handleSearch() { this.params.page = 1 this.fetchGroupList() }, handleFetch() { this.groupId = 0 this.fetchGroupList() } } } </script>
(2)groups/components/groupForm.vue
<template> <div class="group-form-container"> <el-dialog :visible.sync="visible" :title="title" @close="handleClose"> <el-form ref="groupForm" :model="form" :rules="rules" label-width="100px"> <el-form-item label="用户组:" prop="name"> <el-input v-model="form.name" placeholder="请输入用户组"/> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="resetForm">重置</el-button> </el-form-item> </el-form> </el-dialog> </div> </template> <script> import { addGroup, modifyGroup } from '@/api/group' export default { name: 'GroupForm', props: { value: { type: Boolean, default: false }, gid: { type: Number, default: 0 }, gname: { type: String, default: '' } }, data() { return { visible: false, groupId: 0, form: { name: '' }, rules: { name: [ { required: true, message: '请输入用户组', trigger: 'blur' } ] } } }, watch: { value(val) { this.visible = val }, gid(val) { if (val < 0) return this.groupId = val } }, methods: { resetForm() { this.$refs.groupForm.resetFields() }, handleClose() { this.visible = false this.groupId = 0 this.resetForm() this.$emit('input', false) }, submitForm() { this.$refs.groupForm.validate((valid) => { if (valid) { this.save() } else { console.log('error submit!!') return false } }) }, save() { if (this.groupId === 0) { this.create() } else { this.update() } }, create() { addGroup(this.form).then(() => { this.$message({ message: `添加用户组 ${this.form.name} 成功`, type: 'success' }) this.handleClose() this.$emit('fetch') }) }, update() { modifyGroup(this.groupId, this.form).then(() => { this.$message({ message: `修改用户组 ${this.form.name} 成功`, type: 'success' }) this.handleClose() this.$emit('fetch') }) } } } </script>
(3)api/group.js
import request from '@/utils/request' export function getGroupList(params) { return request({ url: '/api/groups/', method: 'get', params }) } export function addGroup(data) { return request({ url: '/api/groups/', method: 'post', data }) } export function modifyGroup(id, data) { return request({ url: `/api/groups/${id}/`, method: 'patch', data }) }
效果如下实现添加了:
3.修改用户组
(1)groups/index.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.name" placeholder="搜索用户组" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button type="primary" @click="handleAddGroup">增加用户组</el-button> </el-col> </el-row> <el-table :data="groupData" stripe style=" 100%"> <el-table-column type="index" width="50" /> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleModifyGroup(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <GroupForm v-model="groupFormVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> </div> </template> <script> import { getGroupList } from '@/api/group' import GroupForm from './components/groupForm' export default { name: 'Groups', components: { GroupForm }, data() { return { groupData: [], total: 0, params: { page: 1, name: '' }, groupFormVisible: false, groupId: 0, groupName: '' } }, created() { this.fetchGroupList() }, methods: { fetchGroupList() { getGroupList(this.params).then(res => { this.groupData = res.results this.total = res.count }) }, handleChange(val) { this.params.page = val this.fetchGroupList() }, handleSearch() { this.params.page = 1 this.fetchGroupList() }, handleAddGroup() { this.groupId = 0 this.groupName = '' this.groupFormVisible = true }, handleFetch() { this.groupId = 0 this.fetchGroupList() }, handleModifyGroup(obj) { this.groupId = obj.id this.groupName = obj.name this.groupFormVisible = true } } } </script>
(2)groups/components/groupForm.vue
<template> <div class="group-form-container"> <el-dialog :visible.sync="visible" :title="title" @close="handleClose"> <el-form ref="groupForm" :model="form" :rules="rules" label-width="100px"> <el-form-item label="用户组:" prop="name"> <el-input v-model="form.name" placeholder="请输入用户组"/> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="resetForm">重置</el-button> </el-form-item> </el-form> </el-dialog> </div> </template> <script> import { addGroup, modifyGroup } from '@/api/group' export default { name: 'GroupForm', props: { value: { type: Boolean, default: false }, gid: { type: Number, default: 0 }, gname: { type: String, default: '' } }, data() { return { visible: false, groupId: 0, form: { name: '' }, rules: { name: [ { required: true, message: '请输入用户组', trigger: 'blur' } ] } } }, computed: { title() { if (this.groupId === 0) return '创建用户组' else return '修改用户组' } }, watch: { value(val) { this.visible = val }, gid(val) { if (val < 0) return this.groupId = val }, gname(val) { if (val === '') return this.form.name = val } }, methods: { resetForm() { this.$refs.groupForm.resetFields() if (this.groupId === 0) this.form.name = '' }, handleClose() { this.visible = false this.groupId = 0 this.resetForm() this.$emit('input', false) }, submitForm() { this.$refs.groupForm.validate((valid) => { if (valid) { this.save() } else { console.log('error submit!!') return false } }) }, save() { if (this.groupId === 0) { this.create() } else { this.update() } }, create() { addGroup(this.form).then(() => { this.$message({ message: `添加用户组 ${this.form.name} 成功`, type: 'success' }) this.handleClose() this.$emit('fetch') }) }, update() { modifyGroup(this.groupId, this.form).then(() => { this.$message({ message: `修改用户组 ${this.form.name} 成功`, type: 'success' }) this.handleClose() this.$emit('fetch') }) } } } </script>
效果如下图:
三. 将用户添加至指定用户组
https://element.eleme.io/#/zh-CN/component/select#methods--基础多选组件
1.指定角色页面实现--前端
(1).users/components/assignGroup.vue
<template> <div class="assign-group"> <el-dialog :visible.sync="visible" title="指定角色" @close="handleClose"> <el-form ref="addUserForm" label-width="100px"> <el-form-item label="用户名:"> <el-input v-model="userName" readonly/> </el-form-item> <el-form-item label="姓名:"> <el-select v-model="userGroups" multiple placeholder="请选择" style="100%"> <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id"/> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="handleSubmit">提交</el-button> </div> </el-dialog> </div> </template> <script> import { getGroupList, updateUserGroups } from '@/api/group' export default { name: 'AssignGroup', props: { value: { type: Boolean, default: false }, userId: { type: Number, default: 0 }, userName: { type: String, default: '' } }, data() { return { visible: false, options: [], userGroups: [] } }, watch: { value(val) { if (val <= 0) return this.visible = val this.fetchGroupList() } }, methods: { handleClose() { this.visible = false this.$emit('input', false) setTimeout(() => { this.options = [] this.userGroups = [] }, 500) }, fetchGroupList() { getGroupList({ page_size: 0 }).then(res => { this.options = res }) },
handleSubmit() {
console.log(this.userId, this.userGroups)
this.handleClose()
}
}
}
</script>
(2)users/user.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.username" placeholder="搜索用户名" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button type="primary" @click="addUserVisible=true">添加用户</el-button> </el-col> </el-row> <el-table :data="userList" stripe style=" 100%"> <el-table-column prop="username" label="username"/> <el-table-column prop="name" label="姓名"/> <el-table-column prop="phone" label="电话"/> <el-table-column prop="email" label="email"/> <el-table-column prop="is_active" label="状态"> <template slot-scope="scope"> <el-switch v-model="scope.row.is_active" @change="handleUserStatusChange(scope.row)"/> </template> </el-table-column> <el-table-column prop="last_login" label="last_login"/> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleAssignGroup(scope.row)">指定角色</el-button> <el-button type="text" size="small" @click="handleModify(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <AddUserForm v-model="addUserVisible" @fetch="handleFetch" /> <ModifyUserForm v-model="modifyUserVisible" :user-id="userId" @fetch="handleFetch" /> <AssignGroup v-model="assignGroupVisible" :user-id="userId" :user-name="userName" /> </div> </template> <script> import { getUserList, modifyUser } from '@/api/user' import AddUserForm from './components/addUserForm' import ModifyUserForm from './components/modifyUser' import AssignGroup from './components/assignGroup' export default { name: 'UserList', components: { AddUserForm, ModifyUserForm, AssignGroup }, data() { return { userList: [], addUserVisible: false, modifyUserVisible: false, userId: 0, userName: '', total: 0, params: { page: 1, username: '' }, assignGroupVisible: false } }, created() { this.fetchUserList() }, methods: { fetchUserList() { getUserList(this.params).then(res => { this.userList = res.results this.total = res.count }) }, handleUserStatusChange(obj) { modifyUser(obj.id, { is_active: obj.is_active }).then(() => { this.$message({ message: `修改 ${obj.name} 的状态成功`, type: 'success' }) }) }, handleFetch() { this.fetchUserList() }, handleModify(obj) { this.userId = obj.id this.modifyUserVisible = true }, handleChange(val) { this.params.page = val this.fetchUserList() }, handleSearch() { this.params.page = 1 this.fetchUserList() }, handleAssignGroup(obj) { this.userId = obj.id this.userName = obj.name this.assignGroupVisible = true } } } </script>
(3)users/components/modifyUser.vue
效果如下图:
这样前端就写好了!!
2.后端接口
(1)groups/views.py:
from django.contrib.auth.models import Group from django.contrib.auth import get_user_model from rest_framework import viewsets, mixins, response,status from .serializers import GroupSerializer, UserGroupsSerializer from .filters import GroupFilter from users.serializers import UserSerializer User = get_user_model() class GroupViewset(viewsets.ModelViewSet): queryset = Group.objects.all() serializer_class = GroupSerializer filter_class = GroupFilter filter_fields = ("name",) class UserGroupsViewset(viewsets.GenericViewSet, mixins.UpdateModelMixin, mixins.RetrieveModelMixin): """ retrive: 获取当前用户的呢的用户组列表 update: 修改当前用户的角色(角色) """ queryset = User.objects.all() serializer_class = UserGroupsSerializer def retrieve(self, request, *args, **kwargs): userObj = self.get_object() queryset = userObj.groups.all() page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return response.Response(serializer.data) def update(self, request, *args, **kwargs): userObj = self.get_object() groupIds = request.data.get("gids", []) # userObj.groups = Group.objects.filter(id__in=groupIds) userObj.groups.set(Group.objects.filter(id__in=groupIds)) return response.Response(status=status.HTTP_204_NO_CONTENT)
(2)groups/serializers.py:
from django.contrib.auth.models import Group from rest_framework import serializers class GroupSerializer(serializers.ModelSerializer): """ group序列化类 """ class Meta: model = Group fields = ("id", "name") class UserGroupsSerializer(serializers.Serializer): """ group序列化类 """ id = serializers.ReadOnlyField() name = serializers.ReadOnlyField() class Meta: model = Group fields = ("id", "name")
(3)groups/router.py:
from rest_framework.routers import DefaultRouter from .views import GroupViewset, UserGroupsViewset group_router = DefaultRouter() group_router.register('groups', GroupViewset, basename="groups") group_router.register('userGroups', UserGroupsViewset, basename="userGroups")
3.写前端的api接口:
(1)api/group.js
import request from '@/utils/request' export function getGroupList(params) { return request({ url: '/api/groups/', method: 'get', params }) } export function addGroup(data) { return request({ url: '/api/groups/', method: 'post', data }) } export function modifyGroup(id, data) { return request({ url: `/api/groups/${id}/`, method: 'patch', data }) } // 修改指定用户的角色 export function updateUserGroups(uid, data) { return request({ url: `/api/userGroups/${uid}/`, method: 'patch', data }) } // 获取指定用户的所有角色 export function getUserGroupList(uid, params) { return request({ url: `/api/userGroups/${uid}/`, method: 'get', params }) }
(2)components/assignGroup.vue
<template> <div class="assign-group"> <el-dialog :visible.sync="visible" title="指定角色" @close="handleClose"> <el-form ref="addUserForm" label-width="100px"> <el-form-item label="用户名:"> <el-input v-model="userName" readonly/> </el-form-item> <el-form-item label="姓名:"> <el-select v-model="userGroups" multiple placeholder="请选择" style="100%"> <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id"/> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="handleSubmit">提交</el-button> </div> </el-dialog> </div> </template> <script> import { getGroupList, updateUserGroups, getUserGroupList } from '@/api/group' export default { name: 'AssignGroup', props: { value: { type: Boolean, default: false }, userId: { type: Number, default: 0 }, userName: { type: String, default: '' } }, data() { return { visible: false, options: [], userGroups: [] } }, watch: { value(val) { if (val <= 0) return this.visible = val this.fetchGroupList() this.fetchUserGroups() } }, methods: { handleClose() { this.visible = false this.$emit('input', false) setTimeout(() => { this.options = [] this.userGroups = [] }, 500) }, fetchGroupList() { getGroupList({ page_size: 0 }).then(res => { this.options = res }) }, fetchUserGroups() { getUserGroupList(this.userId, { page_size: 0 }).then(res => { res.forEach((item) => { this.userGroups.push(item.id) }) }) }, handleSubmit() { updateUserGroups(this.userId, { gids: this.userGroups }).then(() => { this.$message({ message: `修改 ${this.userName} 用户组成功`, type: 'success' }) this.handleClose() }) } } } </script>
报500错误:
TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use groups.set() instead
解决:原因Django从1.升级到2.时:orm多对多外健不再用=等赋值,改为set方法
# userObj.groups = Group.objects.filter(id__in=groupIds) userObj.groups.set(Group.objects.filter(id__in=groupIds))
效果如图:
4.用户组成员列表
写后端:
groups/views.py:
from django.contrib.auth.models import Group from django.contrib.auth import get_user_model from rest_framework import viewsets, mixins, response,status from .serializers import GroupSerializer, UserGroupsSerializer from .filters import GroupFilter from users.serializers import UserSerializer User = get_user_model() class GroupViewset(viewsets.ModelViewSet): queryset = Group.objects.all() serializer_class = GroupSerializer filter_class = GroupFilter filter_fields = ("name",) class UserGroupsViewset(viewsets.GenericViewSet, mixins.UpdateModelMixin, mixins.RetrieveModelMixin): """ retrive: 获取当前用户的呢的用户组列表 update: 修改当前用户的角色(角色) """ queryset = User.objects.all() serializer_class = UserGroupsSerializer def retrieve(self, request, *args, **kwargs): userObj = self.get_object() queryset = userObj.groups.all() page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return response.Response(serializer.data) def update(self, request, *args, **kwargs): userObj = self.get_object() groupIds = request.data.get("gids", []) # userObj.groups = Group.objects.filter(id__in=groupIds) userObj.groups.set(Group.objects.filter(id__in=groupIds)) return response.Response(status=status.HTTP_204_NO_CONTENT) class GroupMembersViewset(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.DestroyModelMixin): """ 角色成员管理 retrieve: 获取指定组下的成员列表 """ queryset = Group.objects.all() serializer_class = UserSerializer def retrieve(self, request, *args, **kwargs): groupObj = self.get_object() queryset = groupObj.user_set.all() page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return response.Response(serializer.data) def destroy(self, request, *args, **kwargs): groupObj = self.get_object() userId = request.data.get("uid", 0) ret = {"status":0} try: userObj = User.objects.get(pk=userId) groupObj.user_set.remove(userObj) except User.DoesNotExist: ret["status"] = 1 ret["errmsg"] = "用户错误" return response.Response(ret, status=status.HTTP_200_OK)
groups/router.py:
from rest_framework.routers import DefaultRouter from .views import GroupViewset, UserGroupsViewset, GroupMembersViewset group_router = DefaultRouter() group_router.register('groups', GroupViewset, basename="groups") group_router.register('userGroups', UserGroupsViewset, basename="userGroups") group_router.register('groupMembers', GroupMembersViewset, basename="groupMembers")
groups/serializers.py:
from django.contrib.auth.models import Group from rest_framework import serializers class GroupSerializer(serializers.ModelSerializer): """ group序列化类 """ def to_representation(self, instance): ret = super(GroupSerializer, self).to_representation(instance) ret["members"] = instance.user_set.count() return ret class Meta: model = Group fields = ("id", "name") class UserGroupsSerializer(serializers.Serializer): """ group序列化类 """ id = serializers.ReadOnlyField() name = serializers.ReadOnlyField() class Meta: model = Group fields = ("id", "name")
写前端:
(1)groups/components/groupMembers.vue
<template> <div class="group-members"> <el-dialog :visible.sync="visible" :title="title" @close="handleClose"> <el-table v-loading="loading" :data="memberList" stripe style=" 100%"> <el-table-column type="index"/> <el-table-column prop="phone" label="电话"/> <el-table-column prop="email" label="email"/> <el-table-column prop="name" label="姓名"/> </el-table> <el-row v-show="total>params.page_size" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" :page-size="params.page_size" :current-page.sync="params.page" layout="total, prev, pager, next" background @current-change="handleChange" /> </el-row> </el-dialog> </div> </template> <script> import { getGroupMemberList } from '@/api/group' export default { name: 'GroupMember', props: { value: { type: Boolean, default: false }, gid: { type: Number, default: 0 }, gname: { type: String, default: '' } }, data() { return { visible: false, loading: false, memberList: [], total: 0, params: { page: 1, page_size: 6 } } }, computed: { title() { return `${this.gname} 的成员列表` } }, watch: { value(val) { if (val !== true) return this.visible = val this.params.page = 1 this.fetchGroupMemberList() } }, methods: { handleClose() { this.visible = false this.$emit('input', false) this.$emit('fetch') setTimeout(() => { this.memberList = [] }, 500) }, fetchGroupMemberList() { this.loading = true getGroupMemberList(this.gid, this.params).then(res => { this.memberList = res.results this.total = res.count this.loading = false }) }, handleChange(val) { this.params.page = val this.fetchGroupMemberList() } } } </script>
(2)groups/index.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.name" placeholder="搜索用户组" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button type="primary" @click="handleAddGroup">增加用户组</el-button> </el-col> </el-row> <el-table :data="groupData" stripe style=" 100%"> <el-table-column type="index" width="50" /> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="成员管理"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleGroupMember(scope.row)">成员列表 <el-badge :value="scope.row.members" class="mark" /></el-button> </template> </el-table-column> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleModifyGroup(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <GroupForm v-model="groupFormVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> <GroupMember v-model="groupMemberVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> </div> </template> <script> import { getGroupList } from '@/api/group' import GroupForm from './components/groupForm' import GroupMember from './components/groupMembers' export default { name: 'Groups', components: { GroupForm, GroupMember }, data() { return { groupData: [], total: 0, params: { page: 1, name: '' }, groupFormVisible: false, groupMemberVisible: false, groupId: 0, groupName: '' } }, created() { this.fetchGroupList() }, methods: { fetchGroupList() { getGroupList(this.params).then(res => { this.groupData = res.results this.total = res.count }) }, handleChange(val) { this.params.page = val this.fetchGroupList() }, handleSearch() { this.params.page = 1 this.fetchGroupList() }, handleAddGroup() { this.groupId = 0 this.groupName = '' this.groupFormVisible = true }, handleFetch() { this.groupId = 0 this.fetchGroupList() }, handleModifyGroup(obj) { this.groupId = obj.id this.groupName = obj.name this.groupFormVisible = true }, handleGroupMember(obj) { this.groupId = obj.id this.groupName = obj.name this.groupMemberVisible = true } } } </script>
(3)api/group.js
import request from '@/utils/request' export function getGroupList(params) { return request({ url: '/api/groups/', method: 'get', params }) } export function addGroup(data) { return request({ url: '/api/groups/', method: 'post', data }) } export function modifyGroup(id, data) { return request({ url: `/api/groups/${id}/`, method: 'patch', data }) } // 修改指定用户的角色 export function updateUserGroups(uid, data) { return request({ url: `/api/userGroups/${uid}/`, method: 'patch', data }) } // 获取指定用户的所有角色 export function getUserGroupList(uid, params) { return request({ url: `/api/userGroups/${uid}/`, method: 'get', params }) } // 获取指定用户组下的成员列表 export function getGroupMemberList(gid, params) { return request({ url: `/api/groupMembers/${gid}/`, method: 'get', params }) }
效果如下:
5.从用户组中移除成员
(1)groups/components/groupMembers.vue
<template> <div class="group-members"> <el-dialog :visible.sync="visible" :title="title" @close="handleClose"> <el-table v-loading="loading" :data="memberList" stripe style=" 100%"> <el-table-column type="index"/> <el-table-column prop="phone" label="电话"/> <el-table-column prop="email" label="email"/> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="成员管理"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleRemoveMember(scope.row)">移除成员</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>params.page_size" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" :page-size="params.page_size" :current-page.sync="params.page" layout="total, prev, pager, next" background @current-change="handleChange" /> </el-row> </el-dialog> </div> </template> <script> import { getGroupMemberList, removeGroupMember } from '@/api/group' export default { name: 'GroupMember', props: { value: { type: Boolean, default: false }, gid: { type: Number, default: 0 }, gname: { type: String, default: '' } }, data() { return { visible: false, loading: false, memberList: [], total: 0, params: { page: 1, page_size: 6 } } }, computed: { title() { return `${this.gname} 的成员列表` } }, watch: { value(val) { if (val !== true) return this.visible = val this.params.page = 1 this.fetchGroupMemberList() } }, methods: { handleClose() { this.visible = false this.$emit('input', false) this.$emit('fetch') setTimeout(() => { this.memberList = [] }, 500) }, fetchGroupMemberList() { this.loading = true getGroupMemberList(this.gid, this.params).then(res => { this.memberList = res.results this.total = res.count this.loading = false }) }, handleChange(val) { this.params.page = val this.fetchGroupMemberList() }, handleRemoveMember(obj) { removeGroupMember(this.gid, { uid: obj.id }).then(res => { if (res.status === 0) { this.$message({ message: `从 ${this.gname} 组中移除 ${obj.name} 成功`, type: 'success' }) this.fetchGroupMemberList() } else { this.$message({ message: `从 ${this.gname} 组中移除 ${obj.name} 失败: ${res.errmsg}`, type: 'error' }) } }) } } } </script>
(2)api/groups.js
.... // 从用户组中移除指定用户 export function removeGroupMember(gid, data) { return request({ url: `/api/groupMembers/${gid}/`, method: 'delete', data }) }
效果如下图:
6. 修改用户组权限
https://element.eleme.cn/#/zh-CN/component/transfer#transfer-chuan-suo-kuang
(1)groups/index.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.name" placeholder="搜索用户组" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button type="primary" @click="handleAddGroup">增加用户组</el-button> </el-col> </el-row> <el-table :data="groupData" stripe style=" 100%"> <el-table-column type="index" width="50" /> <el-table-column prop="name" label="姓名"/> <el-table-column fixed="right" label="成员管理"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleGroupMember(scope.row)">成员列表 <el-badge :value="scope.row.members" class="mark" /></el-button> </template> </el-table-column> <el-table-column fixed="right" label="权限管理"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleGroupPermission(scope.row)">修改权限</el-button> </template> </el-table-column> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleModifyGroup(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <GroupForm v-model="groupFormVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> <GroupMember v-model="groupMemberVisible" :gid="groupId" :gname="groupName" @fetch="handleFetch"/> <GroupPermission v-model="groupPermissionVisible" :gid="groupId" :gname="groupName"/> </div> </template> <script> import { getGroupList } from '@/api/group' import GroupForm from './components/groupForm' import GroupMember from './components/groupMembers' import GroupPermission from './components/groupPermission' export default { name: 'Groups', components: { GroupForm, GroupMember, GroupPermission }, data() { return { groupData: [], total: 0, params: { page: 1, name: '' }, groupFormVisible: false, groupMemberVisible: false, groupPermissionVisible: false, groupId: 0, groupName: '' } }, created() { this.fetchGroupList() }, methods: { fetchGroupList() { getGroupList(this.params).then(res => { this.groupData = res.results this.total = res.count }) }, handleChange(val) { this.params.page = val this.fetchGroupList() }, handleSearch() { this.params.page = 1 this.fetchGroupList() }, handleAddGroup() { this.groupId = 0 this.groupName = '' this.groupFormVisible = true }, handleFetch() { this.groupId = 0 this.fetchGroupList() }, handleModifyGroup(obj) { this.groupId = obj.id this.groupName = obj.name this.groupFormVisible = true }, handleGroupMember(obj) { this.groupId = obj.id this.groupName = obj.name this.groupMemberVisible = true }, handleGroupPermission(obj) { this.groupId = obj.id this.groupName = obj.name this.groupPermissionVisible = true } } } </script>
(2)groups/components/groupPermission.vue
<template> <div class="group-permission"> <el-dialog :visible.sync="visible" :title="title" @close="handleClose"> <el-transfer v-model="groupPermission" :titles="transferTitle" :data="data" filterable /> <span slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="handleUpdateGroupPermission">提交修改</el-button> </span> </el-dialog> </div> </template> <script> import { getPermissionList, updateGroupPermissionList, getGroupPermissionList } from '@/api/permission' export default { name: 'GroupPermission', props: { value: { type: Boolean, default: false }, gid: { type: Number, default: 0 }, gname: { type: String, default: '' } }, data() { return { visible: false, data: [], groupPermission: [] } }, computed: { title() { return `修改 ${this.gname} 的权限` }, transferTitle() { return ['权限点', `${this.gname} 的权限`] } }, watch: { value(val) { if (val === false) return this.visible = val this.fetchPermissionList() this.fetchGroupPermissionList() } }, methods: { handleClose() { this.visible = false this.$emit('input', false) setTimeout(() => { this.groupPermission = [] }, 500) }, fetchPermissionList() { getPermissionList({ page_size: 0 }).then(res => { console.log(res) this.data = res }) }, handleUpdateGroupPermission() { updateGroupPermissionList(this.gid, { pids: this.groupPermission }).then(res => { if (res.status === 0) { this.$message({ message: `修改 ${this.gname} 组的权限成功`, type: 'success' }) this.handleClose() } }) }, fetchGroupPermissionList() { getGroupPermissionList(this.gid).then(res => { res.forEach((item) => { this.groupPermission.push(item.key) }) }) } } } </script> <style> .el-transfer-panel{40%} </style>
(3)api/permission.js
import request from '@/utils/request' export function getPermissionList(params) { return request({ url: '/api/permission/', method: 'get', params }) } // 更新指定用户组的权限列表 export function updateGroupPermissionList(gid, data) { return request({ url: `/api/groupPermission/${gid}/`, method: 'patch', data }) } // 获取指定用户组的权限列表 export function getGroupPermissionList(gid, params) { return request({ url: `/api/groupPermission/${gid}/`, method: 'get', params }) }
后端:
(1)settings.py
INSTALLED_APPS = [
'permissions',
(2)apps/permissions/views.py
from rest_framework import viewsets, mixins, response, status from django.contrib.auth.models import Permission, Group from .serializers import PermissionSerializer class PermissionViewset(viewsets.GenericViewSet, mixins.ListModelMixin): """ 权限列表 list: 获取权限列表 """ queryset = Permission.objects.all() serializer_class = PermissionSerializer class GroupPermissionViewset(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.UpdateModelMixin): """ 用户组的权限 retrieve: 返回用户组的权限列表 update: 更新指定用户组的权限 """ queryset = Group.objects.all() serializer_class = PermissionSerializer def retrieve(self, request, *args, **kwargs): groupObj = self.get_object() queryset = groupObj.permissions.all() serializer = self.get_serializer(queryset, many=True) return response.Response(serializer.data) def update(self, request, *args, **kwargs): ret = {"status":0} groupObj = self.get_object() pids = request.data.get("pids",[]) groupObj.permissions = Permission.objects.filter(pk__in=pids) return response.Response(ret, status=status.HTTP_200_OK)
(3)apps/permissions/router.py
from rest_framework.routers import DefaultRouter from .views import PermissionViewset, GroupPermissionViewset permission_router = DefaultRouter() permission_router.register('permission', PermissionViewset, basename="permission") permission_router.register('groupPermission', GroupPermissionViewset, basename="groupPermission")
(4)apps/permissions/serializers.py
from rest_framework import serializers from django.contrib.auth.models import Permission, ContentType class PermissionSerializer(serializers.ModelSerializer): def to_representation(self, instance): ret = {} ret["key"] = instance.id ret["label"] = "{}.{}".format(instance.content_type.app_label, instance.codename) return ret class Meta: model = Permission fields = "__all__"
效果如下:
四.权限控制应用
1.用户登录(gwt)与退出
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc
https://github.com/GetBlimp/django-rest-framework-jwt 官方文档
https://jpadilla.github.io/django-rest-framework-jwt/ 使用文档
后端:
(python36env) [vagrant@CentOS devops]$ pip install djangorestframework-jw 安装
(1)settings.py:
import os import sys import datetime REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'devops.paginations.Pagination', 'PAGE_SIZE': 10, 'DEFAULT_FILTER_BACKENDS': ( 'django_filters.rest_framework.DjangoFilterBackend', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.DjangoModelPermissions', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', } JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=10), 'JWT_AUTH_HEADER_PREFIX': 'JWT', }
(2)devops/urls.py:
from rest_framework_jwt.views import obtain_jwt_token .. from permissions.router import permission_router router.registry.extend(permission_router.registry) ... urlpatterns = [ ... url(r'^api-token-auth/', obtain_jwt_token), ]
(python36env) [vagrant@CentOS devops]$ curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456"}' http://localhost:8000/api-token-auth/ {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo3MSwidXNlcm5hbWUiOiJhZG1pbiIsImV4cCI6MTU5NTIzMjkzNiwiZW1haWwiOiJhZG1pbkA1MXJlYm9vdC5jb20ifQ.hdgMbP-8ohsCayslX_ZvyMo5DUi65DjUcrvXpp5Ps4g"}
(3)users/views.py:
from rest_framework import viewsets, permissions, mixins, response from .serializers import UserSerializer, UserRegSerializer from .filters import UserFilter from django_filters.rest_framework import DjangoFilterBackend from django.contrib.auth import get_user_model User = get_user_model() class UserViewset(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): """ retrieve: 获取指定user记录 list: 获取user列表 update: 更新user记录 partial_update: 更新user的部门字段 destroy: 删除user记录 """ queryset = User.objects.filter(is_superuser=False) serializer_class = UserSerializer filter_class=UserFilter filter_fields = ("username",) class UserRegViewset(viewsets.GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin): """ create: 用户注册 partial_update: 修改密码 update: 修改密码 """ queryset = User.objects.all() serializer_class = UserRegSerializer class UserInfoViewset(viewsets.ViewSet): permission_classes = (permissions.IsAuthenticated,) def list(self, request, *args, **kwargs): data = { "username": self.request.user.username, "name": self.request.user.name, "permission": self.request.user.get_all_permissions() } return response.Response(data)
(4)users/router.py:
from rest_framework.routers import DefaultRouter from .views import UserViewset, UserRegViewset, UserInfoViewset router = DefaultRouter() router.register("users", UserViewset, basename="users") router.register("userReg", UserRegViewset, basename="userReg") router.register("UserInfo", UserInfoViewset, basename="UserInfo")
效果如图:
前端:
(1)src/permission.jss
import router from './router' import store from './store' import NProgress from 'nprogress' // Progress 进度条 import 'nprogress/nprogress.css' import { getToken } from './utils/auth' // Progress 进度条样式 import { Message } from 'element-ui' // import { getToken } from '@/utils/auth' // 验权 const whiteList = ['/login'] // 不重定向白名单 router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { if (to.path === '/login') { next({ path: '/' }) NProgress.done() } else { if (store.getters.name.length === 0) { store.dispatch('GetInfo').then(() => { // 拉取用户信息 next() }).catch((err) => { store.dispatch('FedLogOut').then(() => { Message.error(err || 'Verification failed, please login again') next({ path: '/' }) }) }) } else { next() } } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { next(`/login?redirect=${to.path}`) NProgress.done() } } }) router.afterEach(() => { NProgress.done() // 结束Progress })
(2)login/index.vue
<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left"> <h3 class="title">51reboot运维平台</h3> <el-form-item prop="username"> <span class="svg-container"> <svg-icon icon-class="user" /> </span> <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="请输入用户名" /> </el-form-item> <el-form-item prop="password"> <span class="svg-container"> <svg-icon icon-class="password" /> </span> <el-input :type="pwdType" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" @keyup.enter.native="handleLogin" /> <span class="show-pwd" @click="showPwd"> <svg-icon icon-class="eye" /> </span> </el-form-item> <el-form-item> <el-button :loading="loading" type="primary" style="100%;" @click.native.prevent="handleLogin"> 登陆 </el-button> </el-form-item> </el-form> </div> </template> <script> export default { name: 'Login', data() { return { loginForm: { username: '', password: '' }, loginRules: { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] }, loading: false, pwdType: 'password', redirect: undefined } }, methods: { showPwd() { if (this.pwdType === 'password') { this.pwdType = '' } else { this.pwdType = 'password' } }, handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { this.loading = true this.$store.dispatch('Login', this.loginForm).then(() => { this.loading = false this.$router.push({ path: this.redirect || '/' }) }).catch(() => { this.loading = false }) } else { console.log('error submit!!') return false } }) } } } </script> <style rel="stylesheet/scss" lang="scss"> $bg:#2d3a4b; $light_gray:#eee; /* reset element-ui css */ .login-container { .el-input { display: inline-block; height: 47px; 85%; input { background: transparent; border: 0px; -webkit-appearance: none; border-radius: 0px; padding: 12px 5px 12px 15px; color: $light_gray; height: 47px; &:-webkit-autofill { -webkit-box-shadow: 0 0 0px 1000px $bg inset !important; -webkit-text-fill-color: #fff !important; } } } .el-form-item { border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.1); border-radius: 5px; color: #454545; } } </style> <style rel="stylesheet/scss" lang="scss" scoped> $bg:#2d3a4b; $dark_gray:#889aa4; $light_gray:#eee; .login-container { position: fixed; height: 100%; 100%; background-color: $bg; .login-form { position: absolute; left: 0; right: 0; 520px; max- 100%; padding: 35px 35px 15px 35px; margin: 120px auto; } .tips { font-size: 14px; color: #fff; margin-bottom: 10px; span { &:first-of-type { margin-right: 16px; } } } .svg-container { padding: 6px 5px 6px 15px; color: $dark_gray; vertical-align: middle; 30px; display: inline-block; } .title { font-size: 26px; font-weight: 400; color: $light_gray; margin: 0px auto 40px auto; text-align: center; font-weight: bold; } .show-pwd { position: absolute; right: 10px; top: 7px; font-size: 16px; color: $dark_gray; cursor: pointer; user-select: none; } } </style>
(3)api/login.js
import request from '@/utils/request' export function login(username, password) { return request({ url: '/api-token-auth/', method: 'post', data: { username, password } }) } export function getInfo() { return request({ url: '/api/UserInfo', method: 'get' }) } export function logout() { return request({ url: '/user/logout', method: 'post' }) }
(4)srcstoremodulesuser.js
import { login, getInfo } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' const user = { state: { token: getToken(), name: '', username: '', permission: [], avatar: '', roles: [] }, mutations: { SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { if (name === null) return state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, SET_ROLES: (state, roles) => { state.roles = roles }, SET_USERNAME: (state, username) => { state.username = username }, SET_PERMISSION: (state, permission) => { state.permission = permission } }, actions: { // 登录 Login({ commit }, userInfo) { const username = userInfo.username.trim() return new Promise((resolve, reject) => { login(username, userInfo.password).then(response => { console.log(response) setToken(response.token) commit('SET_TOKEN', response.token) resolve() }).catch(error => { reject(error) }) }) }, // 获取用户信息 GetInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => { commit('SET_USERNAME', response.username) commit('SET_NAME', response.name) commit('SET_PERMISSION', response.permission) resolve(response) }).catch(error => { reject(error) }) }) }, // 登出 LogOut({ commit, state }) { return new Promise((resolve, reject) => { commit('SET_TOKEN', '') commit('SET_NAME', '') removeToken() resolve() }) }, // 前端 登出 FedLogOut({ commit }) { return new Promise(resolve => { commit('SET_TOKEN', '') commit('SET_NAME', '') removeToken() resolve() }) } } } export default user
(5)src/utils/auth.js
import Cookies from 'js-cookie' import store from '../store' const TokenKey = 'token' export function getToken() { return Cookies.get(TokenKey) } export function setToken(token) { return Cookies.set(TokenKey, token) } export function removeToken() { return Cookies.remove(TokenKey) } export function checkPermission(perm) { if (store.getters.permission.indexOf(perm) > -1) { return true } return false }
(6)utils/request.js
import axios from 'axios' import { Message } from 'element-ui' // import { Message, MessageBox } from 'element-ui' import store from '../store' import { getToken } from '@/utils/auth' import router from '../router' // 创建axios实例 const service = axios.create({ baseURL: process.env.BASE_API, // api 的 base_url timeout: 5000 // 请求超时时间 }) // request拦截器 service.interceptors.request.use( config => { if (store.getters.token) { config.headers['Authorization'] = 'JWT ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } return config }, error => { // Do something with request error console.log(error) // for debug Promise.reject(error) } ) // response 拦截器 service.interceptors.response.use( response => { /** * code为非20000是抛错 可结合自己业务进行修改 */ return response.data }, error => { console.log('err' + error) // for debug if (!error.response) { Message.error('系统错误') } else if (error.response.status === 401) { store.dispatch('FedLogOut').then(() => { router.push({ path: '/login' }) }) } else if (error.response.status === 403) { Message({ message: '权限拒绝', type: 'error', duration: 800, onClose: function() { router.push({ path: '/' }) } }) } else if (error.response.status === 400) { Message({ message: '认证失败, 用户名或密码错误', type: 'error' }) } else if (error.response.status === 500) { Message({ message: '服务内部错误', type: 'error' }) } return Promise.reject(error) } ) export default service
(7)users/user.vue
<template> <div class="app-container"> <el-row> <el-col :span="12"> <el-input v-model="params.username" placeholder="搜索用户名" @keyup.enter.native="handleSearch"> <el-button slot="append" icon="el-icon-search" @click="handleSearch"/> </el-input> </el-col> <el-col :span="12" align="right" style="padding-right:20px;"> <el-button v-if="addGroupPerm" type="primary" @click="addUserVisible=true">添加用户</el-button> </el-col> </el-row> <el-table :data="userList" stripe style=" 100%"> <el-table-column prop="username" label="username"/> <el-table-column prop="name" label="姓名"/> <el-table-column prop="phone" label="电话"/> <el-table-column prop="email" label="email"/> <el-table-column prop="is_active" label="状态"> <template slot-scope="scope"> <el-switch v-model="scope.row.is_active" @change="handleUserStatusChange(scope.row)"/> </template> </el-table-column> <el-table-column prop="last_login" label="last_login"/> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="text" size="small" @click="handleAssignGroup(scope.row)">指定角色</el-button> <el-button type="text" size="small" @click="handleModify(scope.row)">修改</el-button> </template> </el-table-column> </el-table> <el-row v-show="total>10" type="flex" justify="center" style="padding-top:20px;"> <el-pagination :total="total" layout="prev, pager, next" background @current-change="handleChange" /> </el-row> <AddUserForm v-model="addUserVisible" @fetch="handleFetch" /> <ModifyUserForm v-model="modifyUserVisible" :user-id="userId" @fetch="handleFetch" /> <AssignGroup v-model="assignGroupVisible" :user-id="userId" :user-name="userName" /> </div> </template> <script> import { getUserList, modifyUser } from '@/api/user' import { checkPermission } from '@/utils/auth' import AddUserForm from './components/addUserForm' import ModifyUserForm from './components/modifyUser' import AssignGroup from './components/assignGroup' export default { name: 'UserList', components: { AddUserForm, ModifyUserForm, AssignGroup }, data() { return { userList: [], addUserVisible: false, modifyUserVisible: false, userId: 0, userName: '', total: 0, params: { page: 1, username: '' }, assignGroupVisible: false } }, computed: { addGroupPerm: function() { return checkPermission('users.add_user') } }, created() { this.fetchUserList() }, methods: { fetchUserList() { getUserList(this.params).then(res => { this.userList = res.results this.total = res.count }) }, handleUserStatusChange(obj) { modifyUser(obj.id, { is_active: obj.is_active }).then(() => { this.$message({ message: `修改 ${obj.name} 的状态成功`, type: 'success' }) }) }, handleFetch() { this.fetchUserList() }, handleModify(obj) { this.userId = obj.id this.modifyUserVisible = true }, handleChange(val) { this.params.page = val this.fetchUserList() }, handleSearch() { this.params.page = 1 this.fetchUserList() }, handleAssignGroup(obj) { this.userId = obj.id this.userName = obj.name this.assignGroupVisible = true } } } </script>
(8) src/store/getters.js
const getters = { sidebar: state => state.app.sidebar, device: state => state.app.device, token: state => state.user.token, avatar: state => state.user.avatar, name: state => state.user.name, roles: state => state.user.roles, permission: state => state.user.permission } export default getters
(9)src/router/index.js
import Vue from 'vue' import Router from 'vue-router' // in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; // detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading Vue.use(Router) /* Layout */ import Layout from '../views/layout/Layout' /** * hidden: true if `hidden:true` will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu, whatever its child routes length * if not set alwaysShow, only more than one route under the children * it will becomes nested mode, otherwise not show the root menu * redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb * name:'router-name' the name is used by <keep-alive> (must set!!!) * meta : { title: 'title' the name show in submenu and breadcrumb (recommend set) icon: 'svg-name' the icon show in the sidebar, } **/ export const constantRouterMap = [ { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', name: 'Dashboard', children: [{ path: 'dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'dashboard', icon: 'example' } }] }, { path: '/users', component: Layout, name: 'users', meta: { title: '用户管理', icon: 'example' }, children: [ { path: 'user', name: 'user', component: () => import('@/views/users/user'), meta: { title: '用户' } }, { path: 'groups', name: 'groups', permission: 'resources.add_ip', component: () => import('@/views/groups'), meta: { title: '用户组' } } ] }, { path: '*', redirect: '/404', hidden: true } ] export default new Router({ mode: 'history', scrollBehavior: () => ({ y: 0 }), routes: constantRouterMap })
(10)layout/components/sidebar/sidebarltem.vue
<template> <div class="menu-wrapper"> <template v-for="item in routes" v-if="!item.hidden&&item.children"> <router-link v-if="item.children.length===1 && !item.children[0].children && !item.alwaysShow" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name"> <el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}"> <svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon> <span v-if="item.children[0].meta&&item.children[0].meta.title">{{item.children[0].meta.title}}</span> </el-menu-item> </router-link> <el-submenu v-else :index="item.name||item.path" :key="item.name"> <template slot="title"> <svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon> <span v-if="item.meta&&item.meta.title">{{item.meta.title}}</span> </template> <template v-for="child in item.children" v-if="!child.hidden"> <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item> <router-link v-else-if="handleCheckPermission(child.permission)" :to="item.path+'/'+child.path" :key="child.name"> <el-menu-item :index="item.path+'/'+child.path"> <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon> <span v-if="child.meta&&child.meta.title">{{child.meta.title}}</span> </el-menu-item> </router-link> </template> </el-submenu> </template> </div> </template> <script> import { checkPermission } from '@/utils/auth' export default { name: 'SidebarItem', props: { routes: { type: Array }, isNest: { type: Boolean, default: false } }, data() { return { onlyOneChild: null } }, methods: { handleCheckPermission(perm) { if (perm) { return checkPermission(perm) } return true } } } </script>
效果如下图:
总结:
用户权限的三种使用方法: