Django后台数据获取展示

发布于:2024-08-17 ⋅ 阅读:(97) ⋅ 点赞:(0)

​ 续接Django REST Framework,使用Vite构建Vue3的前端项目

1.跨域获取后台接口并展示

  • 安装Axios
npm install axios --save
  • 前端查看后端所有定义的接口
// 访问后端定义的可视化Api接口文档
http://ip:8000/docs/
// 定义的学生类信息
http://ip:8000/api/v1/students/

  • 前端对后端发起请求
// 导入 axios
import axios from 'axios'

// 页面一加载便获取axios请求

<script>
    // 获取所有信息
    const getStudents = () => {
        // axios请求
        axios.get('http://192.168.20.110:8000/api/v1/students/').then((res)=>{
            // 请求成功
            console.log('成功',res)
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
    }
    // 定义页面加载时自动执行的函数
    const autoRun= () => {
        // 获取所有信息
        getStudents() 
    }
    // 调用自动执行函数执行
    autoRun() 
</script>
  • 后端配置运行环境

// 或者直接在0.0.0.0:8000下运行
python manage.py runserver 0.0.0.0:8000

// 后端开启跨域,安装组件
pip install django-cors-headers
// 注册
INSTALLED_APPS[
    'corsheaders'
]
// 添加到中间件中
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',# 跨域请求
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# ======跨域配置======
# 后端是否支持对 Cookis访问
CORS_ALLOW_CREDENTIALS = True
# 白名单地址
CORS_ORIGIN_WHITELIST = (
    'http://192.168.20.110:8080',
)

  • 跨域获取所有信息
// 导入 axios
import axios from 'axios'

// 添加信息获取提示
import { ElMessage } from 'element-plus'

// 页面一加载便获取axios请求

<script>
    // 获取所有信息
    const getStudents = () => {
        // axios请求
        axios.get('http://192.168.20.110:8000/api/v1/students/').then((res)=>{
            // 请求成功
            // console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                ElMessage({
                    message: '数据加载成功!',
                    type: 'success',
                })
            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
    }
    // 定义页面加载时自动执行的函数
    const autoRun= () => {
        // 获取所有信息
        getStudents() 
    }
    // 调用自动执行函数执行
    autoRun() 
</script>

  • 分页优化
<script lang="ts" setup>
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"
import axios from 'axios'
// 添加信息获取提示
import { ElMessage } from 'element-plus'


// 定义存储集合
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        {
            value:1,
            label:'计算机学院'
        },
        {
            value:2,
            label:'外语学院'
        },
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
        {
            value:1,
            label:'计算机专业'
        },
        {
            value:2,
            label:'外语专业'
        },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref(""),
    // ===表格区域定义====
    students: reactive([
        
    ]),
    // =====分页====
    // 当前页
    currentsPage: ref(1),
    // 每页显示的数据量
    pageSize: ref(15),
    // 总数据量所有记录条数
    total: ref(0),
});

// 分页中修改每页的pageSize
const handleSizeChange=(size: any)=>{
  // 修改记录条数
  Data.pageSize = size
  // 重新获取所有信息
  getStudents()
}

// 分页中修改每页的currentsPage
const handleCurrentChange=(page: any)=>{
  // 修改当前页
  Data.currentsPage = page
  // 重新获取所有信息
  getStudents()
}

// ======前后端交互======
// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize
        }
        // axios请求
        axios.get('http://192.168.20.110:8000/api/v1/students/',{params:params}).then((res)=>{
            // 请求成功
            console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
    }
    // 定义页面加载时自动执行的函数
    const autoRun= () => {
        // 获取所有信息
        getStudents() 
    }
    // 调用自动执行函数执行
    autoRun() 

</script>
  • 院系/专业信息展示优化

学生表里只记录了专业的id,专业表里记录了院系的id,要使在学生明细表中展示专业和院系信息则需要在后端序列化专业表和院系表

// serializer.py
# 导入模块
# models中的类与接口转换
from rest_framework import serializers
from studentweb.models import *

# 学院序列化类
class FacultySerializer(serializers.ModelSerializer):

    class Meta:
        model = Faculty
        fields = '__all__'

# 专业序列化类
class MajorSerializer(serializers.ModelSerializer):
    # 序列化学院id
    faculty = FacultySerializer()
    class Meta:
        model = Major
        fields = '__all__'

# 学生序列化类
class StudentSerializer(serializers.ModelSerializer):
    # 序列化专业信息
    major = MajorSerializer()
    class Meta:
        model = Student
        fields = '__all__'
// 专业的名字:major.name
// 院系的名字:major.faculty.name
{
      "sno": "950001",
      "major": {
        "id": 2,
        "faculty": {
          "id": 1,
          "name": "计算机学院"
        },
        "name": "计算机网络"
      },
      "name": "王晓宇",
      "gender": "男",
      "birthday": "2022-03-01",
      "mobile": "13099881122",
      "email": "wangxiaoyu@qq.com",
      "address": "上海市闵行区春都路88号",
      "image": ""
    },
  •  表格绑定展示
// 更改前端表格展示数据
 <el-table-column prop="major.faculty.name" label="院系" align="center" width="120" />
 <el-table-column prop="major.name" label="专业" align="center" width="120" />
  • 表格过长数据处理
// 过长信息不展示完全使用'...'显示

// 标签添加 :show-overflow-tooltip="true"属性

2.Axios模块化请求

  • request脚本实现
// 新建 /src/utils/request.ts
// 封装 axios代码实现模块化

// 1.导入
import axios from 'axios'
// 2.创建一个Axios的app
const request = axios.create({
    // 定义基本 URL
    baseURL: 'http://192.168.__.___:8000/api/v1/',
    // 设置超时
    timeout: 5000,
    
})

// 3.暴露
export default request 
// 添加request.ts配置的axios

<script lang="ts" setup>
// 导入request
import request from "../../utils/request";
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"

// 添加信息获取提示
import { ElMessage } from 'element-plus'


// 定义存储集合
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        {
            value:1,
            label:'计算机学院'
        },
        {
            value:2,
            label:'外语学院'
        },
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
        {
            value:1,
            label:'计算机专业'
        },
        {
            value:2,
            label:'外语专业'
        },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref(""),
    // ===表格区域定义====
    students: reactive([
        
    ]),
    // =====分页====
    // 当前页
    currentsPage: ref(1),
    // 每页显示的数据量
    pageSize: ref(15),
    // 总数据量所有记录条数
    total: ref(0),
});

// 分页中修改每页的pageSize
const handleSizeChange=(size: any)=>{
  // 修改记录条数
  Data.pageSize = size
  // 重新获取所有信息
  getStudents()
}

// 分页中修改每页的currentsPage
const handleCurrentChange=(page: any)=>{
  // 修改当前页
  Data.currentsPage = page
  // 重新获取所有信息
  getStudents()
}

// ======前后端交互======
// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize
        }
        // axios请求
        request.get('students/',{params:params}).then((res)=>{
            // 请求成功
            console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
    }
    // 定义页面加载时自动执行的函数
    const autoRun= () => {
        // 获取所有信息
        getStudents() 
    }
    // 调用自动执行函数执行
    autoRun() 

</script>
  • Api数据集及请求类型以及url优化 接口实现
// 新建 /src/api/students.ts(majors.ts/facultys.ts)

// 导入
import request from "../utils/request";

// RESTFUL ---> 6个接口【获取所有,添加,获取某一个,修改,删除】

// ====获取所有====
const getAll = (params ?: any) => {
    // request
    return request({
        method: "GET",
        url: "/students",
        params,
    })
}

// ====添加====
const add= (data: any) => {
    // request
    return request({
        method: "POST",
        url: "/students",
        data,
    })
}
// ====获取单一数据====
const getOne=(id: any) => {
    // request
    return request({
        method: "GET",
        url: `/students/${id}`,
    })
}
// ====修改====
const edit=(id: any, data: any) => {
    // request
    return request({
        method: "PUT",
        url: `/students/${id}`,
        data
    })
}
// ====删除====
const del = (id: any) => {
    // request
    return request({
        method: "DELETE",
        url: `/students/${id}`,
    })
}
export default {
    getAll,
    add,
    getOne,
    edit,
    del,
    // 也可以直接在此处定义其他的 api
}

// 新建 index.ts用于导入所有api

// 导入所有
import studentApi from "./students"
import majorsApi from "./majors"
import facultysApi from "./facultys"

// 封装并发布
export default {
    studentApi,
    majorsApi,
    facultysApi,    
}

// 使用 上述定义的接口

<script lang="ts" setup>
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"
// 导入API
import indexApi from '../../api/index'
// 添加信息获取提示
import { ElMessage } from 'element-plus'


// 定义存储集合
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        {
            value:1,
            label:'计算机学院'
        },
        {
            value:2,
            label:'外语学院'
        },
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
        {
            value:1,
            label:'计算机专业'
        },
        {
            value:2,
            label:'外语专业'
        },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref(""),
    // ===表格区域定义====
    students: reactive([
        
    ]),
    // =====分页====
    // 当前页
    currentsPage: ref(1),
    // 每页显示的数据量
    pageSize: ref(15),
    // 总数据量所有记录条数
    total: ref(0),
});

// 分页中修改每页的pageSize
const handleSizeChange=(size: any)=>{
  // 修改记录条数
  Data.pageSize = size
  // 重新获取所有信息
  getStudents()
}

// 分页中修改每页的currentsPage
const handleCurrentChange=(page: any)=>{
  // 修改当前页
  Data.currentsPage = page
  // 重新获取所有信息
  getStudents()
}

// ======前后端交互======
// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize
        }
        // axios请求
        indexApi.studentApi.getAll(params).then((res)=>{
            // 请求成功
            console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
    }
    // 定义页面加载时自动执行的函数
    const autoRun= () => {
        // 获取所有信息
        getStudents() 
    }
    // 调用自动执行函数执行
    autoRun() 

</script>

<template>
    <!-- 1.顶部查询区域   styple="display: flax;"横向显示-->
    <el-form :inline="true"  class="demo-form-inline">
    <el-form-item label="查询条件">
      <el-input v-model="Data.q_str" placeholder="请输入查询条件" clearable />
    </el-form-item>
    <!-- 动态获取院系信息 -->
    <el-form-item label="院系">
      <el-select v-model="Data.FacultySelected" placeholder="请选择院系">
        <el-option 
        v-for="item in Data.FacultyOptions"
        :key="item.value"
        :label="item.label"
        :value="item.value" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item label="专业">
      <el-select v-model="Data.MajorSelected" placeholder="请选择专业">
        <el-option 
        v-for="item in Data.MajorOptions"
        :key="item.value"
        :label="item.label"
        :value="item.value" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- <el-form-item label="Activity time">
      <el-date-picker
        type="date"
        placeholder="Pick a date"
        clearable
      />
    </el-form-item> -->
    <el-form-item>
      <el-button type="primary">
        <!-- 引入方法1 -->
        <el-icon><component class="icons" is="Search"></component></el-icon>
        <span>查询</span></el-button>
      <el-button type="primary">
        <!-- 引入方法2 -->
        <el-icon><Finished /></el-icon>
        <span>全部</span></el-button>
      <el-button type="primary">
        <el-icon><Pointer /></el-icon>
        <span>添加</span></el-button>
    </el-form-item>
</el-form>
    <!-- 2.表格信息部分 -->
    <el-table :data="Data.students" stripe border style="width: 100%" :header-cell-style="{ backgroundColor:'#409EFF',color:'#FFF',FontSize:'14px' }">
        <el-table-column label="序号" type="index" align="center" width="60" />
        <el-table-column prop="sno" label="学号" align="center" width="80" />
        <el-table-column prop="name" label="姓名" align="center" width="80" />
        <el-table-column prop="gender" label="性别" align="center" width="80"  />
        <el-table-column prop="birthday" label="出生日期" align="center" width="180" />
        <el-table-column prop="major.faculty.name" label="院系" align="center" width="120" :show-overflow-tooltip="true"/>
        <el-table-column prop="major.name" label="专业" align="center" width="120" />
        <el-table-column prop="mobile" label="电话" align="center" width="140" />
        <el-table-column prop="email" label="Email" align="center" width="180" :show-overflow-tooltip="true" />
        <el-table-column prop="address" label="地址" align="center" :show-overflow-tooltip="true"/>
        <!-- 按钮区域 -->
        <el-table-column label="操作" align="center">
            <el-button type="primary" :icon="More" circle size="small"/>
            <el-button type="warning" :icon="Edit" circle size="small"/>
            <el-button type="danger" :icon="Delete" circle size="small"/>
        </el-table-column>

    </el-table>
    <!-- 3.分页 currentPage4当前页 pageSize4每页大小 total记录条数 handleSizeChange改变每页大小 handleCurrentChange改变当前页  -->
    <el-pagination style="margin-top: 28px;"
    background
      v-model:current-page="Data.currentsPage"
      v-model:page-size="Data.pageSize"
      :page-sizes="[10,12,15,17,20,25,40,50]"
      layout="total, sizes, prev, pager, next, jumper"
      :total="Data.total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
</template>

<style scoped>
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.demo-form-inline .el-select {
  --el-select-width: 220px;
}

</style>
  • 院系联级下拉框实现
// 1.重新定义FacultyOptions 结构
// 定义存储集合
var Data = reactive({
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        { id:"",name:""}, 
    ])
}),
// 定义页面加载时自动执行的函数

// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize
        }
        // axios请求
        indexApi.studentApi.getAll(params).then((res)=>{
            // 请求成功
            console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
     }

// 2.获取所有院系信息
const getFacultys = ()=> {
    // 请求
    indexApi.facultysApi.getAll().then((res)=>{
    // console.log(res.data)
    Data.FacultyOptions = res.data.results;
  })
    
}

    
// 3.定义页面加载时自动执行的函数
const autoRun= () => {
    // 获取所有信息
    getStudents() 
    // 获取院系填充信息
    getFacultys()
}



// 调用自动执行函数执行
autoRun() 

4.更改院系标签值
<!-- 动态获取院系信息  clearable filterable 添加过滤-->
    <el-form-item label="院系">
      <el-select v-model="Data.FacultySelected" placeholder="请选择院系" clearable filterable>
        <el-option 
        v-for="item in Data.FacultyOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
  • 专业联动院系下拉框实现 
<script lang="ts" setup>
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"
// 导入API
import indexApi from '../../api/index'
// 添加信息获取提示
import { ElMessage } from 'element-plus'



// 定义存储集合
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        { id:"",name:""}, 
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
     {
      id: "",
      name: ""
    },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref(""),


// ======前后端交互======

// 获取所有院系信息
const getFacultys = ()=> {
  
  // 请求
  indexApi.facultysApi.getAll().then((res)=>{
    // console.log(res.data)
    Data.FacultyOptions = res.data.results;
  })
};
// 获取院系对应的专业信息
const getMajors = () => {
  // 准备条件
  let params = {
    // http://192.168.20.110:8000/api/v1/majors/?name=&faculty=1
    name:'',
    faculty: Data.FacultySelected
  }
  // 请求
  indexApi.majorsApi.getAll(params).then((res)=>{
    // console.log(res.data.results)
    Data.MajorOptions = res.data.results;
  })
};


// 定义页面加载时自动执行的函数
const autoRun= () => {
    // 获取所有信息
    getStudents() 
    // 获取院系填充信息
    getFacultys()
}
// 调用自动执行函数执行
autoRun() 

</script>

<template>
    <!-- 动态获取院系信息 -->
    <el-form-item label="院系">
      <el-select v-model="Data.FacultySelected" placeholder="请选择院系" clearable filterable @change="getMajors">
        <el-option 
        v-for="item in Data.FacultyOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item label="专业">
      <el-select v-model="Data.MajorSelected" placeholder="请选择专业">
        <el-option 
        v-for="item in Data.MajorOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
   
      </el-select>
    </el-form-item>
    
    <el-form-item>
      <el-button type="primary">
        <!-- 引入方法1 -->
        <el-icon><component class="icons" is="Search"></component></el-icon>
        <span>查询</span></el-button>
      <el-button type="primary">
        <!-- 引入方法2 -->
        <el-icon><Finished /></el-icon>
        <span>全部</span></el-button>
      <el-button type="primary">
        <el-icon><Pointer /></el-icon>
        <span>添加</span></el-button>
    </el-form-item>
</el-form>
</template>

<style scoped>
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.demo-form-inline .el-select {
  --el-select-width: 220px;
}

</style>
// 后端注意 filter.py Major筛选类的定义

# Major的filter类
class MajorFilter(FilterSet):
    # 重写支持模糊匹配的字段
    name = filters.CharFilter(field_name='name', lookup_expr='icontains')

    class Meta:
        model = Major
        fields = ('name','faculty')
  • 面向对象思维优化Api接口
// 新建 api/apibase.ts 定义基础类

// 导入
import request from "../utils/request";

export default class Apibase {

    // 定义属性
    public name : string;

    // 构造函数(器)
    constructor(name : string) {
        this.name = name;
    }

    // 方法
    public getAll = (params?: any) => {
        // request请求
        return request({
            method: "GET",
            url: `${this.name}`,
            params,
        })
    }

    // ====添加====
    public add= (data: any) => {
        // request
        return request({
            method: "POST",
            url: `${this.name}`,
            data,
        })
    }
    // ====获取单一数据====
    public getOne=(id: any) => {
        // request
        return request({
            method: "GET",
            url: `${this.name}/${id}`,
        })
    }
    // ====修改====
    public edit=(id: any, data: any) => {
        // request
        return request({
            method: "PUT",
            url: `${this.name}/${id}`,
            data
        })
    }
    // ====删除====
    public del = (id: any) => {
        // request
        return request({
            method: "DELETE",
            url: `${this.name}/${id}`,
        })
    }
}
// 调用基础类 api/index.ts


// 导入基础类
import Apibase from "./apibase"

// 实例化对象
let studentApi = new Apibase("students");
let majorsApi = new Apibase("majors");
let facultysApi = new Apibase("facultys");

// 封装并发布
export default {
    studentApi,
    majorsApi,
    facultysApi,    
}

// 删除/注销 facultys.ts、majors.ts、students.ts
  • 联级信息过滤实现查询和展示全部的功能
// 输入关键字筛选实现

// 后端输入筛选字段设置
# 学生视图
class StudentViewSet(ModelViewSet):
    """
    create:
    创建院系信息
    retrieve:
    获取院系信息详情数据
    update:
    完整更新院系信息
    partial_update:
    部分更新院系信息
    destroy:
    删除院系信息
    list:
    获取所有院系信息
    """
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 分页
    pagination_class = MyPageNumberPagination
    filter_class = StudentFilter
    # 指定查找匹配的字段
    search_fields = ('sno','name','mobile','email','address')

// 前端获取所有信息中新增search筛选条件

// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize,
            // 一对多,可以匹配多个字段 ?search=...
            search: Data.q_str
        }
        // axios请求
        indexApi.studentApi.getAll(params).then((res)=>{
            // 请求成功
            // console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
};

// 绑定点击事件
<el-button type="primary" @click="getStudents">
    <el-icon><component class="icons" is="Search"></component></el-icon>
    <span>查询</span>
</el-button>
// 点击全部按钮查询实现
// 定义函数
// 点击全部 清楚删选条件
const listAllStudent = () =>{
  // 清空查询条件
  Data.q_str = ''
  // 重新获取所有信息
  getStudents()
}

// 绑定按钮

<el-button type="primary" @click="listAllStudent">
    <el-icon><Finished /></el-icon>
    <span>全部</span>
</el-button>
# 院系筛选条件 http://ip/api/v1/students/?sno=&name=&mobile=&major=&faculty=5

# 更改后端代码 filter.py

class StudentFilter(FilterSet):
    # 重写支持模糊匹配的字段
    sno = filters.CharFilter(field_name='sno', lookup_expr='icontains')
    name = filters.CharFilter(field_name='name', lookup_expr='icontains')
    mobile = filters.CharFilter(field_name='mobile', lookup_expr='icontains')
    # 添加专业字段匹配
    major = filters.CharFilter(field_name='major')
    # 专业表跳转到学院表
    faculty = filters.CharFilter(field_name='major__faculty')

    class Meta:
        model = Student
        # 新增专业字段
        fields = ('sno','name','mobile','major')

// 前端绑定
// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize,
            // 一对多,可以匹配多个字段 ?search=...
            search: Data.q_str,
            // 添加=====院系字段 提供学院的 key====
            faculty: Data.FacultySelected,
        }
        // axios请求
        indexApi.studentApi.getAll(params).then((res)=>{
            // 请求成功
            // console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
};

// 点击全部 清楚删选条件
const listAllStudent = () =>{
  // 清空查询条件
  Data.q_str = ''
  // ======清空院系条件======
  Data.FacultySelected = ''
  // 重新获取所有信息
  getStudents()
}
// 专业信息筛选

// 获取所有信息
const getStudents = () => {
    // 定义一个集合存储分页参数
        let params = {
            page: Data.currentsPage,
            size: Data.pageSize,
            // 一对多,可以匹配多个字段 ?search=...
            search: Data.q_str,
            // 添加院系字段 提供学院的 key
            faculty: Data.FacultySelected,
            // 添加专业筛选
            major: Data.MajorSelected
        }
        // axios请求
        indexApi.studentApi.getAll(params).then((res)=>{
            // 请求成功
            // console.log('成功',res)
            // 判断是否成功
            if (res.status==200){
                // 成功执行
                Data.students = res.data.results
                // 将记录总数绑定到分页的total
                Data.total = res.data.count
                // 成功提示
                ElMessage({
                  message: '数据加载成功!',
                  type: 'success',
                })

            }
        }).catch((error)=>{
            // 请求失败
            console.log('失败',error)
        })
};

// 点击全部 清楚删选条件
const listAllStudent = () =>{
  // 清空查询条件
  Data.q_str = ''
  Data.FacultySelected = ''
  Data.MajorSelected = ''
  // 重新获取所有信息
  getStudents()
}
  • 配置Vue实例的全局变量
// 挂载全局对象
// Vue2.0-->Vue.prototype.$api = api 
// Vue3.0-->app.config.globalProperties.api = api
// =====main.ts定义全局变量=====
// 导入所有数据集的 Api
import Apibase from './api/apibase'
// 将 Api挂载到全局的属性
app.config.globalProperties.api = Apibase
// =====全局定义Api使用 info.vue=====
import { ref, reactive, getCurrentInstance } from "vue"
// 获取当前实例
const indexApi = (getCurrentInstance() as any).proxy.api;
  • 请求拦截器和响应拦截器

请求拦截器: 自动添加身份验证的token 


// ====请求拦截器 utils/request.ts=====

// ====封装 axios代码实现模块化====
// 1.导入
import axios from 'axios'
// 2.创建一个Axios的app
const request = axios.create({
    // 定义基本 URL
    baseURL: 'http://192.168.20.110:8000/api/v1/',
    // 设置超时
    timeout: 5000,
    
})

// ====请求拦截器(发请求request)基于上边创建的request====
request.interceptors.request.use(
    (config: any) => {
        // 获取本地loalstorage中的toten
        let token = localStorage.getItem('toten')
        // 判断是否有token
        if(token) {
            // 如果有token,就在请求头中添加token
            config.headers.common['token'] = token
        }
        // 返回
        return config
    },
    (error: any) => {
        Promise.reject(error);
    },
);
// ====响应拦截器(反馈response) 基于上边创建的request====
request.interceptors.response.use();
// 3.暴露
export default request 


// ==========后端配置 settings.py========
CORS_ALLOW_HEADERS = (
    'token',
    'jwt',
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)


响应拦截器: 自动提示所有的请求报错信息

// ====封装 axios代码实现模块化====
// 1.导入
import axios from 'axios'
import {ElMessage} from 'element-plus'
// 2.创建一个Axios的app
const request = axios.create({
    // 定义基本 URL
    baseURL: 'http://192.168.20.110:8000/api/v1/',
    // 设置超时
    timeout: 5000,
    
})

// ====请求拦截器(发请求request)基于上边创建的request====
request.interceptors.request.use(
    (config: any) => {
        // 获取本地loalstorage中的toten
        let token = localStorage.getItem('toten')
        // 判断是否有token
        if(token) {
            // 如果有token,就在请求头中添加token
            config.headers.common['token'] = token
        }
        // 返回
        return config
    },
    (error: any) => {
        Promise.reject(error);
    },
);

// ====响应拦截器(反馈response) 基于上边创建的request====
request.interceptors.response.use(
    (response: any) => response,
    (error: any) => {
        if (error && error.response) {
            error.data = {};
            switch (error.response.status) {
                case 400:
                    error.data.msg = '错误请求';
                    ElMessage.error(error.data.msg)
                    break
                case 401:
                    error.data.msg = '未授权,请重新登录';
                    ElMessage.error(error.data.msg)
                    break
                case 403:
                    error.data.msg = '拒绝访问';
                    ElMessage.error(error.data.msg)
                    break
                case 404:
                    error.data.msg = '请求错误,未找到该资源';
                    ElMessage.error(error.data.msg)
                    break
                case 405:
                    error.data.msg = '请求方法未允许';
                    ElMessage.error(error.data.msg)
                    break
                case 408:
                    error.data.msg = '请求超时';
                    ElMessage.error(error.data.msg)
                    break
                case 500:
                    error.data.msg = '服务器端出错';
                    ElMessage.error(error.data.msg)
                    break
                case 501:
                    error.data.msg = '网络未实现';
                    ElMessage.error(error.data.msg)
                    break
                case 502:
                    error.data.msg = '网络错误';
                    ElMessage.error(error.data.msg)
                    break
                case 503:
                    error.data.msg = '服务不可用';
                    ElMessage.error(error.data.msg)
                    break
                case 504:
                    error.data.msg = '网络超时';
                    ElMessage.error(error.data.msg)
                    break
                case 505:
                    error.data.msg = 'http版本不支持该请求';
                    ElMessage.error(error.data.msg)
                    break
                default:
                    error.data.msg = `连接错误${error.response.status}`;
                    ElMessage.error(error.data.msg)
            }
        } else {
            error.data.msg = "连接到服务器失败";
            ElMessage.error(error.data.msg)
        }
        return Promise.reject(error);
    }
);
// 3.暴露
export default request 

3.信息弹出层的布局和数据填充实现

  • 信息弹出层实现
// ========定义存储集合========
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        { id:"",name:""}, 
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
     {
      id: "",
      name: ""
    },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref(""),
    // ===表格区域定义====
    students: reactive([
        
    ]),
    // =====分页====
    // 当前页
    currentsPage: ref(1),
    // 每页显示的数据量
    pageSize: ref(15),
    // 总数据量所有记录条数
    total: ref(0),
    // =====弹出层-----
    dialogFormVisible: ref(false),
});
// ====弹出层(顶部添加按钮)====
const addStudent = () => {
  Data.dialogFormVisible = true;
}

    

<!-- 4.弹出层 -->
    <el-dialog v-model="Data.dialogFormVisible" title="学生信息" width="40%">
    <el-form :inline="true">
      <el-form-item label="学号:">
        <el-input placeholder="请输入" />
      </el-form-item>
      <el-form-item label="姓名:">
        <el-input placeholder="请输入" />
      </el-form-item>
      <el-form-item label="性别:" style="width: 20%;">
        <el-select placeholder="请选择">
          <el-option label="男" value="shanghai" />
          <el-option label="女" value="beijing" />
        </el-select>
      </el-form-item>
    
      <!-- 动态获取院系信息 -->
    <el-form-item style="width: 32%;" label="院系:">
      <el-select v-model="Data.FacultySelected" placeholder="请选择院系" clearable filterable @change="getMajors">
        <el-option 
        v-for="item in Data.FacultyOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item style="width: 32%;" label="专业:">
      <el-select v-model="Data.MajorSelected" placeholder="请选择专业" clearable filterable>
        <el-option 
        v-for="item in Data.MajorOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
   
    <el-form-item label="电话:">
      <el-input placeholder="请输入" />
    </el-form-item>
    <el-form-item label="邮箱:">
      <el-input placeholder="请输入" />
    </el-form-item>
     <!-- 日期 -->
     <el-form-item style="width: 32%;" label="出生日期:">
      <el-date-picker
        type="date"
        placeholder="请选择日期"
        clearable
      />
    </el-form-item>
    <el-form-item style="width: 57%;" label="家庭住址:">
      <el-input  type="textarea" />
    </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button>取消</el-button>
        <el-button>提交</el-button>
      </div>
    </template>
  </el-dialog>

  • 优化弹出层展示实现

// 弹出层定义
// 定义存储集合
var Data = reactive({
    // =====弹出层-----
    dialogFormVisible: ref(false),
    // 定义弹出层标题
    layerTitle: ref(""),
    // 定义表单双向绑定的值
    studentform: reactive({
      sno: ref(""),
      name: ref(""),
      gender: ref(""),
      birthday: ref(""),
      faculty: reactive({
        // 存储从后台获取的所有院系信息
        FacultyOptions: reactive([
          { id:"",name:""}, 
        ]),
        // 存储选择院系后的值
        FacultySelected: ref(""),
        }),
      major: reactive({
        // 存储从后台获取的所有专业信息
        MajorOptions: reactive([
        {
          id: "",
          name: ""
        },
        ]),
        // 存储选择专业后的值
        MajorSelected: ref(""),
        }),
      mobile: ref(""),
      email: ref(""),
      address: ref(""),
    })
})

// ====弹出层(顶部添加按钮)====
const addStudent = () => {
  Data.dialogFormVisible = true;
  // 修改标题
  Data.layerTitle = '【添加学生信息】'
}

// *************************
// 获取所有院系信息
const getFacultysTC = ()=> {
  
  // 请求
  indexApi.facultysApi.getAll().then((res)=>{
    // console.log(res.data)
    Data.studentform.faculty.FacultyOptions = res.data.results;
  })
};
// 获取院系对应的专业信息
const getMajorsTC = () => {
  // 准备条件
  let params = {
    // http://192.168.20.110:8000/api/v1/majors/?name=&faculty=1
    name:'',
    faculty: Data.studentform.faculty.FacultySelected
  }
  // 请求
  indexApi.majorsApi.getAll(params).then((res)=>{
    // console.log(res.data.results)
    Data.studentform.major.MajorOptions = res.data.results;
  })
};


// 关闭弹出层
const closeLayer = () => {
  Data.dialogFormVisible = false;
}


// 定义页面加载时自动执行的函数
const autoRun= () => {
    // 获取所有信息
    getStudents() 
    // 获取院系填充信息
    getFacultys()
    // 获取弹出层院系填充信息
    getFacultysTC()
}
// 调用自动执行函数执行
autoRun() 


// 动态标题绑定
<!-- 标题 -->
<template #title>
    <div style="font-size: 18px; color: #409eff; font-weight: bold; text-align: left">{{ Data.layerTitle}}</div>
</template>

// 表单数据显示
<!-- 4.弹出层 -->
    <el-dialog v-model="Data.dialogFormVisible" width="40%">
      <!-- 标题 -->
       <template #title>
          <div style="font-size: 18px; color: #409eff; font-weight: bold; text-align: left">{{ Data.layerTitle}}</div>
       </template>
        <!--先在form上绑定 Data.studentform  -->
    <el-form v-model="Data.studentform" :inline="true">
      <!-- 然后在每一个值上进行绑定 -->
      <el-form-item label="学号:">
        <el-input v-model="Data.studentform.sno" placeholder="请输入" />
      </el-form-item>
      <el-form-item label="姓名:">
        <el-input v-model="Data.studentform.name" placeholder="请输入" />
      </el-form-item>
      <el-form-item label="性别:" style="width: 20%;">
        <el-select v-model="Data.studentform.gender" placeholder="请选择">
          <el-option label="男" value="shanghai" />
          <el-option label="女" value="beijing" />
        </el-select>
      </el-form-item>
    
      <!-- 动态获取院系信息 -->
    <el-form-item style="width: 32%;" label="院系:">
      <el-select v-model="Data.studentform.faculty.FacultySelected" placeholder="请选择院系" clearable filterable @change="getMajorsTC">
        <el-option 
        v-for="item in Data.studentform.faculty.FacultyOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item style="width: 32%;" label="专业:">
      <el-select v-model="Data.studentform.major.MajorSelected" placeholder="请选择专业" clearable filterable>
        <el-option 
        v-for="item in Data.studentform.major.MajorOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
   
    <el-form-item label="电话:">
      <el-input v-model="Data.studentform.mobile" placeholder="请输入" />
    </el-form-item>
    <el-form-item label="邮箱:">
      <el-input v-model="Data.studentform.email" placeholder="请输入" />
    </el-form-item>
     <!-- 日期 -->
     <el-form-item style="width: 32%;" label="出生日期:">
      <el-date-picker
        type="date"
        placeholder="请选择日期"
        clearable
        v-model="Data.studentform.birthday"
      />
    </el-form-item>
    <el-form-item style="width: 57%;" label="家庭住址:">
      <el-input v-model="Data.studentform.address"  type="textarea" />
    </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeLayer">取消</el-button>
        <el-button>提交</el-button>
      </div>
    </template>
  </el-dialog>
  • 三种状态加载弹出层实现
var Data = reactive({
    // 弹出层中表单元素是否可编辑定义
    isView: ref(false), // 是否为查看状态 默认不是
    sEdit: ref(false), // 是否为编辑状态 默认不是
})

// 查看信息
const viewStudent = (row: any) => {
  Data.dialogFormVisible = true;
  // 修改标题
  Data.layerTitle = '【查看学生信息】'
  // 编辑状态
  Data.isEdit = false
  // 查看状态
  Data.isView = true
}

// 编辑信息
 const editStudent = (row: any) => {
  Data.dialogFormVisible = true;
  // 修改标题
  Data.layerTitle = '【编辑学生信息】'
  // 编辑状态
  Data.isEdit = true
  // 查看状态
  Data.isView = false
}


<!-- 按钮区域 -->
<el-table-column label="操作" align="center">
    <el-button type="primary" :icon="More" @click="viewStudent" circle size="small"/>
    <el-button type="warning" :icon="Edit" @click="editStudent" circle size="small"/>
    <el-button type="danger" :icon="Delete" circle size="small"/>
</el-table-column>

<!-- 4.弹出层 -->
    <el-dialog v-model="Data.dialogFormVisible" width="40%">
      <!-- 标题 -->
       <template #title>
          <div style="font-size: 18px; color: #409eff; font-weight: bold; text-align: left">{{ Data.layerTitle}}</div>
       </template>
        <!--先在form上绑定 Data.studentform  -->
    <el-form v-model="Data.studentform" :inline="true">
      <!-- 然后在每一个值上进行绑定 -->
      <el-form-item label="学号:">
        <!-- 设置字段是否可编辑 :disabled="Data.isEdit || Data.isView" 若一个为 True则输入框即为禁用状态 -->
        <el-input v-model="Data.studentform.sno" :disabled="Data.isEdit || Data.isView" placeholder="请输入" />
      </el-form-item>
      <el-form-item label="姓名:">
        <el-input v-model="Data.studentform.name" :disabled="Data.isView" placeholder="请输入" />
      </el-form-item>
      <el-form-item label="性别:" style="width: 20%;">
        <el-select v-model="Data.studentform.gender" :disabled="Data.isView" placeholder="请选择">
          <el-option label="男" value="shanghai" />
          <el-option label="女" value="beijing" />
        </el-select>
      </el-form-item>
    
      <!-- 动态获取院系信息 -->
    <el-form-item style="width: 32%;" label="院系:">
      <el-select v-model="Data.studentform.faculty.FacultySelected" :disabled="Data.isView" placeholder="请选择院系" clearable filterable @change="getMajorsTC">
        <el-option 
        v-for="item in Data.studentform.faculty.FacultyOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item style="width: 32%;" label="专业:">
      <el-select v-model="Data.studentform.major.MajorSelected" :disabled="Data.isView" placeholder="请选择专业" clearable filterable>
        <el-option 
        v-for="item in Data.studentform.major.MajorOptions"
        :key="item.id"
        :label="item.name"
        :value="item.id" />
        <!-- <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" /> -->
      </el-select>
    </el-form-item>
   
    <el-form-item label="电话:">
      <el-input v-model="Data.studentform.mobile" :disabled="Data.isView" placeholder="请输入" />
    </el-form-item>
    <el-form-item label="邮箱:">
      <el-input v-model="Data.studentform.email" :disabled="Data.isView" placeholder="请输入" />
    </el-form-item>
     <!-- 日期 -->
     <el-form-item style="width: 32%;" label="出生日期:">
      <el-date-picker
        type="date"
        placeholder="请选择日期"
        clearable
        v-model="Data.studentform.birthday"
        :disabled="Data.isView"
      />
    </el-form-item>
    <el-form-item style="width: 57%;" label="家庭住址:">
      <el-input v-model="Data.studentform.address" :disabled="Data.isView"  type="textarea" />
    </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeLayer">取消</el-button>
        <el-button v-show="!Data.isView">提交</el-button>
      </div>
    </template>
  </el-dialog>
  • 实现填充数据到弹出层
var Data = reactive({
    // 定义表单双向绑定的值
    studentform: reactive({
      sno: ref(""),
      name: ref(""),
      gender: ref(""),
      birthday: ref(""),
      faculty: ref(""),
      major: ref(""),
      mobile: ref(""),
      email: ref(""),
      address: ref(""),
    }),
    // 弹出层中表单元素是否可编辑定义
    isView: ref(false), // 是否为查看状态 默认不是
    isEdit: ref(false), // 是否为编辑状态 默认不是
});

// 查看学生信息
const viewStudent = (row: any) => {
  // 设定标题
  Data.layerTitle = "【查看学生信息】";
  // 设为查看
  Data.isView = true;
  // 可见
  Data.dialogFormVisible = true;
  // 当前行赋值为studentForm -- 深拷贝
  Data.studentform = JSON.parse(JSON.stringify(row));
};

// 编辑学生信息
const editStudent = (row: any) => {
  // 设定标题
  Data.layerTitle = "【编辑学生信息】";
  // 设为编辑
  Data.isEdit = true;
  // 可见
  Data.dialogFormVisible = true;
  // 当前行赋值为studentForm -- 深拷贝
  console.log(row)
  // Data.studentform = row
  Data.studentform = JSON.parse(JSON.stringify(row));
};


// 关闭弹出层
const closeLayer = () => {
  Data.dialogFormVisible = false;
  // 编辑和查看设置为False
  Data.isEdit = false;
  Data.isView = false;
  // 初始化表单
  Data.studentform.sno = "";
  Data.studentform.name = "";
  Data.studentform.gender = "";
  Data.studentform.birthday = "";
  Data.studentform.major = "";
  Data.studentform.faculty = "";
  Data.studentform.mobile = "";
  Data.studentform.email = "";
  Data.studentform.address = "";
}

<!-- 按钮区域 -->
<el-table-column label="操作" align="center">
    <template #default="scope">
        <el-button type="primary" :icon="More" @click="viewStudent(scope.row)" circle size="small"/>
        <el-button type="warning" :icon="Edit" @click="editStudent(scope.row)" circle size="small"/>
        <el-button type="danger" :icon="Delete" circle size="small"/>
    </template>
</el-table-column>

<!-- 弹出层 -->
<el-dialog v-model="Data.dialogFormVisible" width="40%" @close="closeLayer">
    <!-- 标题部分 -->
    <template #title>
      <div style="font-size: 18px; color: #409eff; font-weight: bold; text-align: left">
        {{ Data.layerTitle }}
      </div>
    </template>


    <el-form
      :model="Data.studentform"
      :inline="true"
      label-width="100px"
      ref="studentFormRef"
    >
      <el-form-item label="学号:" prop="sno">
        <el-input
          v-model="Data.studentform.sno"
          :disabled="Data.isEdit || Data.isView"
          :suffix-icon="Edit"
          placeholder="请输入"
        />
      </el-form-item>
      <el-form-item label="姓名:" prop="name">
        <el-input
          v-model="Data.studentform.name"
          :disabled="Data.isView"
          :suffix-icon="Edit"
          placeholder="请输入"
        />
      </el-form-item>
      <el-form-item label="性别:" style="width: 43%;" prop="gender">
        <el-select
          v-model="Data.studentform.gender"
          :disabled="Data.isView"
          placeholder="请选择"
        >
          <el-option label="男" value="男" />
          <el-option label="女" value="女" />
        </el-select>
      </el-form-item>
      <el-form-item label="出生日期:" prop="birthday" >
        <el-date-picker
          v-model="Data.studentform.birthday"
          type="date"
          placeholder="选择日期"
          style="width: 212px"
          :disabled="Data.isView"
          value-format="YYYY-MM-DD"
        ></el-date-picker>
      </el-form-item>
   
      <el-form-item label="电话:" prop="mobile">
        <el-input
          v-model="Data.studentform.mobile"
          :disabled="Data.isView"
          :suffix-icon="Edit"
          placeholder="请输入"
        />
      </el-form-item>
      <el-form-item label="邮箱:" prop="email">
        <el-input
          v-model="Data.studentform.email"
          :disabled="Data.isView"
          :suffix-icon="Edit"
          placeholder="请输入"
        />
      </el-form-item>
      <el-form-item label="家庭住址:" prop="address">
        <el-input
          v-model="Data.studentform.address"
          :suffix-icon="Edit"
          style="width: 555px"
          :disabled="Data.isView"
          placeholder="请输入"
        ></el-input>
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" v-show="!Data.isView">提交</el-button>
        <el-button @click="closeLayer">取消</el-button>
      </span>
    </template>
  </el-dialog>
  • Cascader级联选择器使用
// *******后端取消默认分页 views.py*******

# 学院视图
class FacultyViewSet(ModelViewSet):
    queryset = Faculty.objects.all()
    serializer_class = FacultySerializer
    # 取消默认分页
    pagination_class = None

# 专业视图
class MajorViewSet(ModelViewSet):
    queryset = Major.objects.all()
    print(queryset)
    serializer_class = MajorSerializer
    # 取消默认分页
    pagination_class = None

// *********前端*********

var Data = reactive({
// 弹出层级联选择器定义
layerFacultyMajor: reactive([]),
// 弹出层选中的专业
layerMajorSelected: ref(""),
})




// 查看学生信息
const viewStudent = (row: any) => {
  // 设定标题
  Data.layerTitle = "【查看学生信息】";
  // 设为查看
  Data.isView = true;
  // 可见
  Data.dialogFormVisible = true;
  // 当前行赋值为studentForm -- 深拷贝
  Data.studentform = JSON.parse(JSON.stringify(row));
  Data.layerMajorSelected = row.major.id
};

// 编辑学生信息
const editStudent = (row: any) => {
  // 设定标题
  Data.layerTitle = "【编辑学生信息】";
  // 设为编辑
  Data.isEdit = true;
  // 可见
  Data.dialogFormVisible = true;
  // 当前行赋值为studentForm -- 深拷贝
  console.log(row)
  // Data.studentform = row
  Data.studentform = JSON.parse(JSON.stringify(row));
  Data.layerMajorSelected = row.major.id
};


// 关闭弹出层
const closeLayer = () => {
  Data.dialogFormVisible = false;
  // 编辑和查看设置为False
  Data.isEdit = false;
  Data.isView = false;
  // 初始化表单
  Data.studentform.sno = "";
  Data.studentform.name = "";
  Data.studentform.gender = "";
  Data.studentform.birthday = "";
  Data.studentform.major = "";
  Data.studentform.faculty = "";
  Data.studentform.mobile = "";
  Data.studentform.email = "";
  Data.studentform.address = "";
  // 初始化表单专业的而选择
  Data.layerMajorSelected = "";
}
// 构建弹出层树状结构的学院和专业
const getTreeMajor = async () =>{
  // 定义集合
  let allFacultys = reactive([]);
  let allMajors = reactive([]);

  // 获取所有院系
  await indexApi.facultysApi.getAll().then((res)=>{
    // console.log(res.data.results)
    allFacultys = res.data;
  });

  // 获取所有专业
  await indexApi.majorsApi.getAll().then((res)=>{
    console.log(res)
    allMajors = res.data;
  });
  // 组合数据
  for (let faculty of allFacultys) {
    // 定义需要的结构
    var obj = reactive({
      value: `${faculty.id}-${faculty.name}`,
      label: faculty.name,
      children: [],
    });
    // 遍历院系填充obj的children
    for (let major of allMajors) {
      // 判断当前专业是否隶属于当前院系
      if (major.faculty.id === faculty.id) {
        //添加
        obj.children.push({
          value: major.id,
          label: major.name,
        });
      }
    }

    // 附加到
    Data.layerFacultyMajor.push(obj);
  }
}

// 定义页面加载时自动执行的函数
const autoRun= () => {
    // 获取所有信息
    getStudents() 
    // 获取院系填充信息
    getFacultys()
    // 获取树状结构的学院和专业
    getTreeMajor()
}
// 调用自动执行函数执行
autoRun() 

// 定义级联标签
<el-form-item label="学院/专业:">
<el-cascader
v-model="Data.layerMajorSelected"
placeholder="选择专业"
:options="Data.layerFacultyMajor"
filterable
style="width: 555px"
:disabled="Data.isView"/>
</el-form-item>

4.表单校检 

  • 表单提交前校检实现
  // 弹出层表单的验证
  rules: reactive({
    sno: [
      { required: true, message: "学号不能为空", trigger: "blur" },
      {
        pattern: /^[9][5]\d{4}$/,
        message: "学号必须要是95开头的6位数字",
        trigger: "blur",
      },
    ],
    name: [
      { required: true, message: "姓名不能为空", trigger: "blur" },
      {
        pattern: /^[\u4e00-\u9fa5]{2,5}$/,
        message: "姓名需要2-5个汉字",
        trigger: "blur",
      },
    ],
    gender: [{ required: true, message: "性别不能为空", trigger: "blur" }],
    birthday: [
      { required: true, message: "出生日期不能为空", trigger: "blur" },
    ],
    major: [{ required: true, message: "专业信息不能为空", trigger: "blur" }],
    mobile: [
      { required: true, message: "手机号码不能为空", trigger: "blur" },
      {
        pattern: /^[1][3456789]\d{9}$/,
        message: "手机号码必须要符合规范",
        trigger: "blur",
      },
    ],
    email: [
      { required: true, message: "邮箱地址不能为空", trigger: "blur" },
      {
        pattern: /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/,
        message: "邮箱地址必须要符合规范",
        trigger: "blur",
      },
    ],
    address: [{ required: true, message: "住址不能为空", trigger: "blur" }],
  }),
  • 应用到指定表单上 
// 标签上绑定rules

<el-form :model="Data.studentform" :inline="true" label-width="100px" :rules="Data.rules">

// 使用 prop="" 应用在指定字段上
<el-form-item label="学号:" prop="sno"></el-form-item>

// 定义表单提交属性 ref="studentFormRef" 
<el-form :model="Data.studentform" :inline="true" label-width="100px" ref="studentFormRef" :rules="Data.rules">

// 获取当前的instance 校检规则
const {proxy} = getCurrentInstance() as any

// 提交按钮绑定
<el-button type="primary" v-show="!Data.isView" @click="commitLayer">提交</el-button>

// 弹出层表单提交
const commitLayer = ()=> {
  // 提交
  proxy.$refs.studentFormRef.validate((vilid: boolean)=> {
    if(vilid){
      alert('符合要求')
    }
  })
}
  • 校检指定信息是否存在
// 校验学号是否存在
const validateSNoExists = (rule: any, value: any, callback: any)=>{
  // 如果是修改,忽略校验
  if(Data.isEdit) callback();
    // 连接Student接口
    indexApi.studentApi.getAll({sno:value}).then((res)=>{
      // 判断是否存在
      if(res.data.count>0){
         callback(new Error("学号已存在!"))
      } else {
         callback()
      }
    })
}

// 添加自定义规则校检
{validator: validateSNoExists, trigger: 'blur' }

// 关闭弹出层中重置表单的校验 
proxy.$refs.studentFormRef.resetFields();

5.实现信息的增删改

  • 信息添加实现
# 后端自定义上传格式
# 自定义上传定义
from rest_framework.response import Response
from rest_framework import status

# 学生视图
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 分页
    pagination_class = MyPageNumberPagination

    # 指定查找匹配的字段
    search_fields = ('sno','name','mobile','email','address')

    # 自定义添加格式
    def create(self, request, *args, **kwargs):
        # 接受传递的值
        rec = request.data
        # 添加
        try:
            Student.objects.create(sno= rec.get('sno'), name=rec.get('name'), gender=rec.get('gender'),birthday=rec.get('birthday'), major_id=rec.get('major'),
                                   mobile= rec.get('mobile'), email=rec.get('email'), address=rec.get('address'),
                                   image=rec.get('image'))
            return Response({'msg': '添加成功!'}, status=status.HTTP_201_CREATED)
        except Exception as e:
            return Response({'error': '添加学生失败'}, status=status.HTTP_400_BAD_REQUEST)
// 前端定义上传数据格式
// 弹出层表单提交
const commitLayer = () => {
  // 提交
  proxy.$refs.studentFormRef.validate((vilid: boolean) => {
    if (vilid) {
      // 添加或者修改
      if(Data.isEdit){
        // 修改
      }else{
        // 添加选择的专业
        Data.studentform.major = Data.layerMajorSelected[1]

        // 添加
        indexApi.studentApi.add(Data.studentform).then((res)=>{
          // 重新加载数据
          getStudents()
          // 关闭弹出层 
          closeLayer();
          // 提示添加成功!
          ElMessage({
            message:"学生添加成功!",
            type:'success'
          })
        })
      }
    }
  })
}
  • 信息修改实现
# 后端定义
    def update(self, request, *args, **kwargs):
        # 接收传递的值
        rec = request.data
        # 添加
        try:
            Student.objects.filter(pk=kwargs.get('pk')).update(name=rec.get('name'), gender=rec.get('gender'),
                                                               birthday=rec.get('birthday'), major_id=rec.get('major'),
                                                               mobile=rec.get('mobile'), email=rec.get('email'),
                                                               address=rec.get('address'), image=rec.get('image'))
            return Response({'msg': '修改成功!'}, status=status.HTTP_201_CREATED)
        except Exception as e:
            return Response({'error': '修改学生失败'}, status=status.HTTP_400_BAD_REQUEST)
// 前端定义

// 弹出层表单提交
const commitLayer = () => {
  // 提交
  proxy.$refs.studentFormRef.validate((vilid: boolean) => {
    if (vilid) {
      // 判断专业是否修改
      if (Data.layerMajorSelected[1]){
          Data.studentform.major = Data.layerMajorSelected[1];
        } else {
          Data.studentform.major = Data.layerMajorSelected;
        }
      // 添加或者修改
      if(Data.isEdit){
        // 修改
        indexApi.studentApi.edit(Data.studentform.sno, Data.studentform).then((res)=>{
            if(res.status === 201){
              // 重新加载数据
              getStudents();
              // 关闭弹出层 
              closeLayer();
              // 提示添加成功!
              ElMessage({
                message:"学生信息修改成功!",
                type:'success'
              })
            }
          })
      }else{
        // 添加选择的专业
        Data.studentform.major = Data.layerMajorSelected[1]

        // 添加
        indexApi.studentApi.add(Data.studentform).then((res)=>{
          // 重新加载数据
          getStudents()
          // 关闭弹出层 
          closeLayer();
          // 提示添加成功!
          ElMessage({
            message:"学生添加成功!",
            type:'success'
          })
        })
      }
    }
  })
}
  • 信息删除实现
// 实现信息删除
const studentDel=(row:any)=>{
  let confirmStr = "您确定要删除学生信息【学号:" + row.sno + "\t 姓名:" + row.name + "】信息吗?";
  ElMessageBox.confirm(confirmStr).then(()=>{
    // 删除
    indexApi.studentApi.del(row.sno).then((res)=>{
      if(res.status === 204){
        // 重新加载数据
        getStudents()
        // 提示删除成功!
        ElMessage({
          message:"学生信息删除成功!",
          type:'success'
        })
      }
    }).catch((error) => {
      // 请求失败
      console.log('失败', error)
      // 提示删除失败
      ElMessage({
        message:"学生信息删除失败!",
        type:'error'
      })
    })

  })
}

// 删除按钮点击
<el-button type="danger" :icon="Delete" @click="studentDel(scope.row)" circle size="small" />

6.图片上传实现

  • 图片上传后端接口实现

# 通用上传文件 /apps/utils/upload.py

"""
本模板实现文件的上传:图片,视频,excel等
当前的上传任务通过upload_file实现,upload_file中有三个参数,分别为:
1) file --- 提交的文件
2) path --- 存储的子目录
3) type --- 文件命名的类型
     1 ---- 时间日期 + 随机值
     2 ---- uuid

返回值描述:
成功: status: True, Data: 新写入的文件名
失败:status: False, error:错误描述

"""
# =========== 导入模块 ===========
from datetime import datetime
import random
import uuid
from django.conf import settings
import os


def get_file_name_random_date():
    """根据日期获取随机值"""
    filename = datetime.now().strftime("%Y-%m-%d").replace("-", "")
    filename += str(random.randint(1000, 9999))
    return filename


def update_file(file, path:str, type:int):
    """
       提供文件的上传
       :param file: 要上传的文件
       :param path: 提供的路径
       :param type: 随机命名的方式  1-- 时间日期随机值  2-- uuid
       :return:
       """
    # 定义一个new_name获取新路径
    new_name = ""
    # 判断
    if type == 1:
        new_name = get_file_name_random_date()
    elif type ==2:
        new_name = uuid.uuid4().hex

    # 拼接路径
    file_name = settings.MEDIA_ROOT + os.path.sep + path + os.path.sep + new_name + os.path.splitext(file.name)[1]
    # 开始写入
    try:
         f = open(file_name, 'wb')
         # 分多次写入
         for i in file.chunks():
             f.write(i)
         # 关闭
         f.close()

         # 返回
         return {'status': True, 'data':  new_name + os.path.splitext(file.name)[1] }

    except Exception as e:
        return {'status': False, 'error': '文件写入磁盘出现异常'}
# 在原有的接口定义中添加自定义接口

# 1.导入模块
from rest_framework.decorators import action
from utils import upload

# 2.在student视图下定义 upload函数
@action(methods=['post'], detail=False)
def upload(self,request, *args, **kwargs):
    # 接受前台传递的文件
    rev_file = request.FILES.get('file')
    # 判断是否粗壮乃
    if not rev_file:
        return Response(status=status.HTTP_400_BAD_REQUEST)
        # 调用
        res = upload.update_file(rev_file,'images', 2)
        # 返回
        return Response(res)
  • 上传图片功能添加到 API
// 对于自定义新增接口,前端新建api接口处理

// 某一类新增字段

import Apibase from "./apibase";
import request from "../utils/request";

// 继承类
export default class studentApi extends Apibase {
    // 构造器
    constructor(name: string) {
        super(name);
    }
    public upload = (data:any)=>{
        return request({
            method: 'post',
            url: `${this.name}/upload/`,
            data
        })
    }
}
  • 图片上传功能实现 
const baseURL = 'http://192.168.20.110:8000/media/image/'
// ==== 头像的上传 ==
const uploadStudentImage = (file:any)=>{
   // ==== 文件在axios中一般封装在formdata类中
   // 1. 定义formData类
   let fileReq = new FormData();
   // 2. 把文件装在formData的容器中
   fileReq.append('file', file.file);

   // 请求 
   indexApi.studentApi.upload(fileReq).then((res)=>{
     Data.studentform.image =  baseURL + res.data.data;
   })
}

const handleAvatarSuccess =() =>{
  // 上传成功的回调
}

const beforeAvatarUpload=() =>{
  // 上传前的的回调
}


<!-- 图片的上传和展示 -->
    <el-upload
      class="avatar-uploader"
      :show-file-list="false"
      :http-request="uploadStudentImage"
      :on-success="handleAvatarSuccess"
      :before-upload="beforeAvatarUpload"
      style="margin: 20px auto"

    >
      <img v-if="Data.studentform.image" :src="Data.studentform.image" class="avatar" />
      <el-icon v-else class="avatar-uploader-icon"><plus /></el-icon>
    </el-upload>


网站公告

今日签到

点亮在社区的每一天
去签到

热门文章