zoukankan      html  css  js  c++  java
  • 二.运维平台之用户组资源

     一.后端实现

    (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>

     效果如下图:

     

     

     

     

     总结:

     

    用户权限的三种使用方法:

     

  • 相关阅读:
    smary里Js正则表达式不正常
    php7下对微信支付退款申请通知的解密处理
    apache下设置deflate/gzip
    从大表里随机取若干行的效率问题
    Ecshop、Discuz! 等开源产品的局限
    mysql实现ORACLE的connect by prior父子递归查询
    SQL计算字符串里的子字符串出现个数
    UCENTER同步登录工作原理和配置要点
    云服务器:西部数码VS阿里云
    用ftp命令实现主机文件批量更新
  • 原文地址:https://www.cnblogs.com/dbslinux/p/13337814.html
Copyright © 2011-2022 走看看