用户功能1

用户功能实现

功能有:

  • 用户列表、分页
  • 添加用户、用户修改、用户删除
  • 激活、禁用用户
  • 用户搜索
  • 角色配置

image

image

image

image

组件和路由

新建src/views/users/UserView.vue

<template>
  <div>用户列表页</div>
</template>
<script>
export default {}
</script>
<style>
</style>

路由配置

import User from '../views/user/UserView' // .vue可以省略
const routes = [
 { path: '/', redirect: '/login' },
 { path: '/login', component: Login },
 {
    path: '/home',
    component: Home,
    redirect: '/welcome', // 访问/home重定向到/welcome,进入子路由
    children: [
     { path: '/welcome', component: Welcome },
     { path: '/users', component: User }
   ]
 }
]

UI组件

参考 https://element.eleme.cn/#/zh-CN/component/breadcrumb

简单卡片 https://element.eleme.cn/#/zh-CN/component/card#jian-dan-qia-pian

搜索框 https://element.eleme.cn/#/zh-CN/component/input#fu-he-xing-shu-ru-kuang

表格 https://element.eleme.cn/#/zh-CN/component/table

src/plugins/element.js

import Vue from 'vue'
import {
  Form, FormItem, Input, Button, Message, Container,
  Header, Aside, Main, Menu, MenuItem, Submenu, Breadcrumb,
  BreadcrumbItem, Card, Row, Col, Table, TableColumn
} from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
Vue.use(Card)
Vue.use(Row)
Vue.use(Col)
Vue.use(Table)
Vue.use(TableColumn)

src/assets/css/main.css 全局样式中加入样式,控制所有面包屑、表格

/* 全局样式表 */
html, body, #app {
    height:100%;
    margin: 0;
    padding: 0;
    min-height: 200px;
}
.el-breadcrumb {
  margin-bottom: 15px;
}
.el-table {
  margin: 15px 0px;
}

UserView.vue

<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <el-card>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-input placeholder="请输入内容" v-model="search">
            <el-button slot="append" icon="el-icon-search"></el-button>
          </el-input>
        </el-col>
        <el-col :span="12">
          <el-button type="primary">增加用户</el-button>
        </el-col>
      </el-row>
      <el-table :data="dataList" border style="width: 100%">
        <el-table-column prop="date" label="日期" width="180"> </el-tablecolumn>
        <el-table-column prop="name" label="姓名" width="180"> </el-tablecolumn>
        <el-table-column prop="address" label="地址"> </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>
<script>
export default {
  data() {
    return {
      search: '', // 搜索
      dataList: [
       {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
       },
       {
          date: '2016-05-04',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1517 弄'
       },
       {
          date: '2016-05-01',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1519 弄'
       }
     ]
   }
 }
}
</script>
<style lang="less" scoped>
</style>

重建User模型类

User模型类是Django内建的,对应auth_user表

CREATE TABLE `auth_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `password` varchar(128) NOT NULL,
  `last_login` datetime(6) DEFAULT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `username` varchar(150) NOT NULL,
  `first_name` varchar(150) NOT NULL,
  `last_name` varchar(150) NOT NULL,
  `email` varchar(254) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `date_joined` datetime(6) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Django可以保持User模型的基础上,增加1对1关系的从表来扩展一些字段。但是,这等于有2个模型类,后面序列化、操作都比较麻烦。

参考 Documentation/Authentication/Customizing authentication https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#extending-the-existing-user-model

Django用户重建User模型

  • 注册应用
  • 一定要使用AUTH_USER_MODEL=我的应用.User模型类 这样的配置
  • 使用django.contrib.auth.get_user_model() 获得模型类

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'user',
]
AUTH_USER_MODEL = 'user.UserProfile'

user/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
# 替换原有User模型类
class UserProfile(AbstractUser):
    class Meta:
        db_table = "auth_user" # 保持原来名字,方便使用
        verbose_name = "用户详细信息"
    #phone就是增加的字段
    phone = models.CharField(max_length=32, verbose_name='电话号码', 
null=True, blank=True)

由于是替换原有的模型类,所以,把数据库所有表情况,重建。

事实上,重建User模型类应该项目开始时就做,而不是现在,应该给一开始就规划好。

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
CREATE TABLE `auth_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `password` varchar(128) NOT NULL,
  `last_login` datetime(6) DEFAULT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `username` varchar(150) NOT NULL,
  `first_name` varchar(150) NOT NULL,
  `last_name` varchar(150) NOT NULL,
  `email` varchar(254) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `date_joined` datetime(6) NOT NULL,
  `phone` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

序列化器

user/serializers.py

from rest_framework import serializers
from .models import UserProfile
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = [
            'id', "password", "is_superuser", "username",
            "email", "is_active", "phone"
            # 'groups', 'user_permissions'
       ]
        extra_kwargs = {
            'username': {'max_length': 16, "min_length":4}, # 重新限定用户名长'password' : {"write_only":True},
            'is_superuser': {'default': False},
            'is_active': {'default':False}
       }

后台视图类

功能有

  • 列表页:用户列表、增加用户
  • 详情页:用户详情页、修改用户、删除用户

所以,采用ModelViewSet比较合适

from rest_framework.viewsets import ModelViewSet
from django.contrib.auth import get_user_model
from .serializers import UserSerializer
class UserViewSet(ModelViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer
    # permission_classes = [IsAdminUser] # 必须是管理员才能管理用户

user/urls.py

from django.urls import path
from .views import menulist_view, UserViewSet
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('', UserViewSet)
urlpatterns = [
    path('menulist/', menulist_view),
] + router.urls

新增用户

注册组件

使用el-dialog对话框组件,参考https://element.eleme.cn/#/zh-CN/component/dialog

import Vue from 'vue'
import {
  Form, FormItem, Input, Button, Message, Container,
  Header, Aside, Main, Menu, MenuItem, Submenu, Breadcrumb,
  BreadcrumbItem, Card, Row, Col, Table, TableColumn,
  Dialog
} from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Dialog)
Dialog组件使用

参考嵌套表单的Dialog

  • 对话框 :visible.sync="addDialogVisible" 控制对话框的显示和消失
  • 对话框关闭 @close 重置表单,利用 $refs 找到表单resetFields()<el-form-item label="邮箱" prob="email">有prob的才能被重置
  • 确定按钮@click 保存数据,发给后台
  • 取消按钮@click让对话框消失
  • <el-table-column type="index"> 为表格增加序号
<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <el-card>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-input placeholder="请输入内容" v-model="search">
            <el-button slot="append" icon="el-icon-search"></el-button>
          </el-input>
        </el-col>
        <el-col :span="12">
          <el-button type="primary" @click="addDialogVisible = true">增加用
户</el-button>
        </el-col>
      </el-row>
      <el-table :data="dataList" border style="width: 100%">
        <el-table-column type="index"> </el-table-column>
        <el-table-column prop="username" label="登录名"> </el-table-column>
        <el-table-column prop="is_active" label="激活"> </el-table-column>
        <el-table-column prop="is_superuser" label="管理员"> </el-tablecolumn>
        <el-table-column prop="phone" label="电话"> </el-table-column>
      </el-table>
    </el-card>
    <!-- 添加用户 -->
    <el-dialog title="增加用户" :visible.sync="addDialogVisible"
@close="resetForm('add')">
      <el-form :model="addForm" :rules="addRules" ref="add" labelwidth="100px">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input type="password" v-model="addForm.checkPass"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-input v-model="addForm.phone"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addForm.email"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="add">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
export default {
  data() {
    var validatePass = (rule, value, callback) => {
      if (value !== this.addForm.password) {
        callback(new Error('两次输入密码不一致!'))
     } else {
        callback()
     }
   }
    return {
      search: '', // 搜索
      dataList: [],
      // 新增
      addDialogVisible: false,
      addForm: { username: '', password: '', checkPass: '', phone: '', 
email: '' },
      addRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        checkPass: [
         { required: true, message: '请再次输入确认密码', trigger: 'blur' },
         { validator: validatePass, trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetForm(name) {
      console.log('closing~~~')
      this.$refs[name].resetFields()
   },
    add() {
      const name = 'add'
      this.$refs[name].validate(async (valid) => {
        if (valid) {
          const { data: response } = await this.$http.post('users/', 
this.addForm)
          if (response.code) {
            return this.$message.error(response.message)
         }
          this.addDialogVisible = false
          this.getList() // 刷新列表
       }
     })
   },
    async getList() {}
 }
}
</script>
<style lang="less" scoped>
</style>

也可以<el-button @click="$refs['add'].resetFields()"> 重置</el-button>

提交后发现成功创建用户,但是,数据库表中密码不对,原来是自动生成的类,没有调用密码生成函数。

手动修改校验器函数,校验完返回一个加密的密码

user/serializers.py

from rest_framework import serializers
from .models import UserProfile
from django.contrib.auth.hashers import make_password
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = [
            'id', "password", "is_superuser", "username",
            "email", "is_active", "phone"
            # 'groups', 'user_permissions'
       ]
        extra_kwargs = {
            'username': {'min_length':4, 'max_length':16},
            "password": {'write_only':True},
            'is_active': {'default': False},
            'is_superuser': {'default': False}
       }
    def validate_password(self, value):
        # 密码强弱分析
        if 4 <= len(value) <= 16:
            return make_password(value)
        raise serializers.ValidationError("The length of password must be between 4 and 16")

用户列表

表格参考https://element.eleme.cn/#/zh-CN/component/table

本组件开始初始化的时候,就需要访问后台,返回用户表格数据。

  • getList() 对后台访问返回用户分页列表
  • created() 中调用getList()
  • el-table中绑定数据:data="dataList"
  • el-table-columnprob="username" 选择字段值,label="登录名"设置标题

UserView.vue如下

<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <el-card>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-input placeholder="请输入内容" v-model="search">
            <el-button slot="append" icon="el-icon-search"></el-button>
          </el-input>
        </el-col>
        <el-col :span="12">
          <el-button type="primary" @click="addDialogVisible = true">增加用
户</el-button>
        </el-col>
      </el-row>
      <el-table :data="dataList" border style="width: 100%">
        <el-table-column type="index"> </el-table-column>
        <el-table-column prop="username" label="登录名"> </el-table-column>
        <el-table-column prop="is_active" label="激活"> </el-table-column>
        <el-table-column prop="is_superuser" label="管理员"> </el-tablecolumn>
        <el-table-column prop="phone" label="电话"> </el-table-column>
      </el-table>
    </el-card>
    <!-- 添加用户 -->
    <el-dialog title="增加用户" :visible.sync="addDialogVisible"
@close="resetForm('add')">
      <el-form :model="addForm" :rules="addRules" ref="add" labelwidth="100px">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input type="password" v-model="addForm.checkPass"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-input v-model="addForm.phone"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addForm.email"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="add">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
export default {
  created() {
    this.getList()
 },
  data() {
    var validatePass = (rule, value, callback) => {
      if (value !== this.addForm.password) {
        callback(new Error('两次输入密码不一致!'))
     } else {
        callback()
     }
   }
    return {
      search: '', // 搜索
      dataList: [],
      // 新增
      addDialogVisible: false,
      addForm: { username: '', password: '', checkPass: '', phone: '', 
email: '' },
      addRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        checkPass: [
         { required: true, message: '请再次输入确认密码', trigger: 'blur' },
         { validator: validatePass, trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetForm(name) {
      console.log('closing~~~')
      this.$refs[name].resetFields()
   },
    add() {
      const name = 'add'
      this.$refs[name].validate(async (valid) => {
        if (valid) {
          const { data: response } = await this.$http.post('users/', 
this.addForm)
          if (response.code) {
            return this.$message.error(response.message)
         }
          this.addDialogVisible = false
          this.getList() // 刷新列表
       }
     })
   },
    async getList() {
      const { data: response } = await this.$http.get('users/')
      if (response.code) {
        return this.$message.error(response.message)
     }
      this.dataList = response
      console.log(this.dataList)
   }
 }
}
</script>
<style lang="less" scoped>
</style>

分页功能

后台分页

rest_framework.pagination.PageNumberPagination 和前台分页组件需要的数据不匹配,前台分页组件往往需要total总数、page-size页条目数、current-page当前页码,这里的数据明显不符合要求。

所以,在后台自定义一个分页类,方便使用。

新建utils/paginations.py,新建自定义分页类PageNumberPagination

from rest_framework import pagination
from rest_framework.response import Response
class PageNumberPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({
            'pagination':{
                'total': self.page.paginator.count,
                'size': self.page_size,
                'page': self.page.number
           },
            'results': data,
       })
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.exceptions.global_exception_handler',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication'
   ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated', # 全局,要求被认证,也就是
登录成功
   ],
    'DEFAULT_PAGINATION_CLASS': 'utils.paginations.PageNumberPagination',
    'PAGE_SIZE': 20,
}
前台分页

组件注册

import Vue from 'vue'
import {
  Form, FormItem, Input, Button, Message, Container,
  Header, Aside, Main, Menu, MenuItem, Submenu, Breadcrumb,
  BreadcrumbItem, Card, Row, Col, Table, TableColumn,
  Dialog, Pagination
} from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Pagination)

分页组件

  • layout=”total, prev, pager, next, jumper” 包含哪些部件。sizes可以不要,一般不让改
  • :total=”1200”
  • :page-size=”20”
  • :current-page=”4”
<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <el-card>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-input placeholder="请输入内容" v-model="search">
            <el-button slot="append" icon="el-icon-search"></el-button>
          </el-input>
        </el-col>
        <el-col :span="12">
          <el-button type="primary" @click="addDialogVisible = true">增加用
户</el-button>
        </el-col>
      </el-row>
      <el-table :data="dataList" border style="width: 100%">
        <el-table-column type="index"> </el-table-column>
        <el-table-column prop="username" label="登录名"> </el-table-column>
        <el-table-column prop="is_active" label="激活"> </el-table-column>
        <el-table-column prop="is_superuser" label="管理员"> </el-tablecolumn>
        <el-table-column prop="phone" label="电话"> </el-table-column>
      </el-table>
      <el-pagination
        @current-change="handleCurrentChange"
        :current-page="pagination.page"
        :page-size="pagination.size"
        layout="total, prev, pager, next, jumper"
        :total="pagination.total"
      >
      </el-pagination>
    </el-card>
    <!-- 添加用户 -->
    <el-dialog title="增加用户" :visible.sync="addDialogVisible"
@close="resetForm('add')">
      <el-form :model="addForm" :rules="addRules" ref="add" labelwidth="100px">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input type="password" v-model="addForm.checkPass"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-input v-model="addForm.phone"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addForm.email"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="add">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
export default {
  created() {
    this.getList()
 },
  data() {
    var validatePass = (rule, value, callback) => {
      if (value !== this.addForm.password) {
        callback(new Error('两次输入密码不一致!'))
     } else {
        callback()
     }
   }
    return {
      search: '', // 搜索
      // 表格
      dataList: [],
      pagination: { page: 1, size: 20, total: 0 },
      // 新增
      addDialogVisible: false,
      addForm: { username: '', password: '', checkPass: '', phone: '', 
email: '' },
      addRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger: 
'blur' }
       ],
        checkPass: [
         { required: true, message: '请再次输入确认密码', trigger: 'blur' },
         { validator: validatePass, trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetForm(name) {
      console.log('closing~~~')
      this.$refs[name].resetFields()
   },
    handleCurrentChange(val) {
      this.getList(val)
   },
    add() {
      const name = 'add'
      this.$refs[name].validate(async (valid) => {
        if (valid) {
          const { data: response } = await this.$http.post('users/', 
this.addForm)
          if (response.code) {
            return this.$message.error(response.message)
         }
          this.addDialogVisible = false
          this.getList() // 刷新列表
       }
     })
   },
    async getList(page = 1) {
      if (!page) {
        page = 1
     }
      const { data: response } = await this.$http.get('users/', {
        params: { page }
     })
      if (response.code) {
        return this.$message.error(response.message)
     }
      this.dataList = response.results
      this.pagination = response.pagination
   }
 }
}
</script>
<style lang="less" scoped>
</style>

image

分页功能,可以选每页多少条数据

UserView全部代码

<template>
  <div>
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <el-card>
      <el-row :gutter="10">
        <el-col :span="6">
          <el-input placeholder="请输入内容" v-model="search">
            <el-button slot="append" icon="el-icon-search" @click="getUserList(1)"></el-button>
          </el-input>
        </el-col>
        <el-col :span="6">
          <el-button type="primary" @click="dialogVisible = true">添加用户</el-button>
        </el-col>
      </el-row>
      <el-table
        :data="dataList"
        stripe
        border
        style="width: 100%"
        class="table"
      >
        <el-table-column
          prop="id"
          label="ID"
          width="180">
        </el-table-column>
        <el-table-column
          prop="username"
          label="姓名"
          width="180">
        </el-table-column>
        <el-table-column
          prop="phone"
          label="电话">
        </el-table-column>
        <el-table-column
          prop="email"
          label="邮箱">
        </el-table-column>
        <el-table-column
          prop="is_active"
          label="是否激活">
          <template #default="{ row }">
            <el-switch
              v-model="row.is_active"
              @change="isActiveChange( row.id, row.is_active )"
            >
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column
          prop="is_superuser"
          label="管理员">
          <template #default="{ row }">
            {{ row.is_superuser ? '管理员' : '普通用户' }}
          </template>
        </el-table-column>
      </el-table>
      <div class="block">
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="page_info.page"
          :page-sizes="[2, 5, 10]"
          :page-size="page_info.size"
          layout="total, sizes, prev, pager, next, jumper"
          :total="page_info.total">
        </el-pagination>
      </div>
    </el-card>
    <el-dialog
      title="添加用户"
      :visible.sync="dialogVisible"
      width="30%"
      @close="resetForm('form')"
    >
      <!--      点击添加用户弹出得界面中得表单 -->
      <el-form ref="form" :model="addForm" label-width="80px" :rules="addRuleForm">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input type="password" v-model="addForm.checkPass"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addForm.email"></el-input>
        </el-form-item>
        <el-form-item label="手机号" prop="phone">
          <el-input v-model="addForm.phone"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
    <el-button @click="resetForm('form')">取 消</el-button>
    <el-button type="primary" @click="addUserData('form')">确 定</el-button>
  </span>
    </el-dialog>
  </div>
</template>

<script lang="js">
export default {
  // 进入页面就调用得
  created () {
    this.getUserList()
  },
  data () {
    // 添加用户时,重复密码验证规则
    var validatePass = (rule, value, callback) => {
      if (value !== this.addForm.password) {
        callback(new Error('两次输入密码不一致!'))
      } else {
        callback()
      }
    }
    // 验证邮箱格式是否正确
    var email = (rule, value, callback) => {
      const mal = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/
      // const mailReg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/
      if (rule.required && !value) {
        return callback(new Error('不能为空'))
      }
      if (value) {
        if (!(mal.test(value))) {
          callback(new Error('请输入正确邮箱'))
        } else {
          callback()
        }
      }
    }
    return {
      isActive: true,
      search: '',
      // 数据显示
      dataList: [],
      pageNumber: 1,
      pageSize: 2,
      // 当前页
      page_info: {},
      // 是否显示添加用户界面的遮罩层
      dialogVisible: false,
      addForm: {
        username: '',
        password: '',
        checkPass: '',
        email: '',
        phone: ''
      },
      addRuleForm: {
        username: [
          {
            required: true,
            message: '请输入用户名',
            trigger: 'blur'
          },
          {
            min: 4,
            max: 16,
            message: '长度在 4 到 16 个字符',
            trigger: 'blur'
          }
        ],
        password: [
          {
            required: true,
            message: '请输入密码',
            trigger: 'blur'
          },
          {
            min: 4,
            max: 16,
            message: '长度在 4 到 16 个字符',
            trigger: 'blur'
          }
        ],
        checkPass: [
          {
            required: true,
            message: '请再次输入密码',
            trigger: 'blur'
          },
          {
            min: 4,
            max: 16,
            message: '长度在 4 到 16 个字符',
            trigger: 'blur'
          },
          {
            validator: validatePass,
            trigger: 'blur'
          }
        ],
        // 验证邮箱格式rule
        email: [
          {
            validator: email,
            tigger: 'blur'
          }
        ]
      }
    }
  },
  methods: {
    // 切换每页xx条调用的方法
    handleSizeChange (val) {
      console.log(`每页 ${val}`)
      this.pageSize = val
      console.log(this.pageNumer, '当前是第几页呢')
      this.getUserList(this.pageNumer, val)
    },
    // 切换页数调用的方法
    handleCurrentChange (val) {
      console.log(`当前页: ${val}`)
      this.pageNumer = val
      this.getUserList(val, this.pageSize)
    },
    // 重置添加用户框内所有的input数据,并退出添加用户遮罩层
    resetForm (name) {
      this.$refs[name].resetFields()
      this.dialogVisible = false
    },
    // 添加用户执行的方法
    addUserData (formName) {
      this.$refs[formName].validate(async valid => {
        if (valid) {
          console.log('验证成功')
          console.log('addform = ', this.addForm)
          const { data: response } = await this.$http.post('users/', this.addForm)
          if (!response.code) {
            console.log(response)
            this.dialogVisible = false
            this.getUserList()
          } else {
            this.$message.error(response.message)
          }
        }
      })
    },
    // 获取分页数据,page为当前第几页,size为一页几条数据
    async getUserList (page, size) {
      const { data: response } = await this.$http.get('users/', {
        params: {
          page,
          size,
          username: this.search
          search: this.searchInputData
        }
      })
      console.log(response, '++++——————+——+——+——+')
      this.page_info = response.paginations
      this.dataList = response.result
    },
    async isActiveChange (id, isActive) {
      console.log(id, isActive, '————————————————————————————————————————————————!!!!!')
      await this.$http.patch(`users/${id}/`, {
        is_active: isActive
      })
    }

  }
}

</script>

<style lang="less" scoped>

</style>
后端分页
from rest_framework import pagination
from rest_framework.response import Response

class PageNumberPagination(pagination.PageNumberPagination):
    #设置指定参数size,前端要和这个参数保持一致
    page_size_query_param = 'size'
    def get_paginated_response(self, data):
        print(data,'__________', data, '+++++++++++')
        return Response({
            'pagination': {
                # 总条数
                'total': self.page.paginator.count,
                #一页有多少条
                'size': self.page_size,
                # 当前是第几页
                'page': self.page.number
            },
            #返回的数据
            'results': data,
        })

URL匹配,在view中如何获取数据,例如url为http://127.0.0.1/users/username/

在view类中用self.kwargs[‘username’] 这样取,如果url为**http://127.0.0.1/users/username?id=1&&name=dujie ,查询字符串的方式,需要用self.query_params.get取**