django-rest-framework+vxe-table实现前后端分离式开发表格交互-完整增删改查功能

1. 效果图:

后端依赖:

Django==3.1.5
django-cors-headers==3.6.0
django-filter==2.4.0
djangorestframework==3.12.2

2. models.py代码

from django.db import models


class PersonInfo(models.Model):
    name = models.CharField(max_length=255)
    email = models.CharField(max_length=255)
    nickname = models.CharField(max_length=255, null=True, blank=True)
    role = models.CharField(max_length=255)
    sex = models.CharField(max_length=255, null=True, blank=True, choices=((1, "男"), (0, "女")))
    age = models.CharField(max_length=255, null=True, blank=True)
    age2 = models.CharField(max_length=255, null=True, blank=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    language = models.CharField(max_length=255, null=True, blank=True)
    birthday = models.CharField(max_length=255, null=True, blank=True)
    color = models.CharField(max_length=255, null=True, blank=True)
    level = models.CharField(max_length=255, null=True, blank=True)
    level2 = models.CharField(max_length=255, null=True, blank=True)
    describe = models.CharField(max_length=255, null=True, blank=True)
    flag = models.CharField(max_length=255, null=True, blank=True)
    flag1 = models.CharField(max_length=255, null=True, blank=True)
    flag2 = models.CharField(max_length=255, null=True, blank=True)
    flag3 = models.CharField(max_length=255, null=True, blank=True)
    num = models.CharField(max_length=255, null=True, blank=True)
    num1 = models.CharField(max_length=255, null=True, blank=True)
    num2 = models.CharField(max_length=255, null=True, blank=True)
    num3 = models.CharField(max_length=255, null=True, blank=True)
    num4 = models.CharField(max_length=255, null=True, blank=True)
    date = models.DateField(null=True, blank=True)
    date1 = models.DateField(null=True, blank=True)
    date2 = models.DateField(null=True, blank=True)
    date3 = models.DateField(null=True, blank=True)
    date4 = models.DateField(null=True, blank=True)
    attr1 = models.CharField(max_length=255, null=True, blank=True)
    attr2 = models.CharField(max_length=255, null=True, blank=True)
    attr3 = models.CharField(max_length=255, null=True, blank=True)
    attr4 = models.CharField(max_length=255, null=True, blank=True)
    attr5 = models.CharField(max_length=255, null=True, blank=True)
    attr6 = models.CharField(max_length=255, null=True, blank=True)
    attr7 = models.CharField(max_length=255, null=True, blank=True)
    attr8 = models.CharField(max_length=255, null=True, blank=True)
    attr9 = models.CharField(max_length=255, null=True, blank=True)
    attr10 = models.CharField(max_length=255, null=True, blank=True)
    attr11 = models.CharField(max_length=255, null=True, blank=True)
    attr12 = models.CharField(max_length=255, null=True, blank=True)
    attr13 = models.CharField(max_length=255, null=True, blank=True)
    attr14 = models.CharField(max_length=255, null=True, blank=True)
    attr15 = models.CharField(max_length=255, null=True, blank=True)
    createDate = models.DateTimeField(auto_now_add=True)
    updateDate = models.DateTimeField(auto_now=True)
    createBy = models.CharField(max_length=255, default="", null=True, blank=True)
    updateBy = models.CharField(max_length=255, default="", null=True, blank=True)

3. serializers.py代码

from rest_framework import serializers
from app01.models import PersonInfo
from datetime import datetime


class PersonInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = PersonInfo
        fields = "__all__"

4. filters.py代码

import django_filters
from django_filters.rest_framework import FilterSet
from .models import PersonInfo


class PersonInfoFilter(FilterSet):
    role = django_filters.CharFilter(field_name='role')
    name = django_filters.CharFilter(field_name='name')
    email = django_filters.CharFilter(field_name='email')
    sex = django_filters.CharFilter(field_name='sex')
    age = django_filters.CharFilter(field_name='age')
    nickname = django_filters.CharFilter(field_name='nickname')

    class Meta:
        model = PersonInfo
        fields = ("role", 'name', 'email', 'sex', 'age')

5.  views.py代码

from app01.models import PersonInfo
from app01.serializers import PersonInfoSerializer
from django_filters.rest_framework import DjangoFilterBackend
from .filters import PersonInfoFilter
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from rest_framework import filters
from rest_framework import generics
import json


class GPersonInfoList(generics.ListCreateAPIView):
    queryset = PersonInfo.objects.all().order_by("id")
    serializer_class = PersonInfoSerializer

    # 分页配置
    pagination_class = PageNumberPagination
    # 过滤、搜索、排序配置
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = PersonInfoFilter
    # search_fields = ["id", "name", "email", "nickname", "role", "amount"]
    ordering_fields = ["name", "role", "age", "updateDate", "createDate"]

    def get(self, request, *args, **kwargs):
        # 动态配置每页显示的条数
        self.pagination_class.page_size = args[0]
        return self.list(request, *args, **kwargs)


class PersonInfoSave(APIView):
    def post(self, request):
        body = request.body.decode("utf8")
        dict_body = json.loads(body)

        # 即新增又修改
        if dict_body.get("insertRecords") and dict_body.get("updateRecords"):
            self.insert_person(dict_body)
            return self.update_person(dict_body)
        elif dict_body.get("insertRecords"):
            # 新增记录
            print(dict_body)
            return self.insert_person(dict_body)
        elif dict_body.get("updateRecords"):
            # 修改记录
            return self.update_person(dict_body)

        if dict_body["removeRecords"]:
            # 删除记录
            return self.remove_person(dict_body)

    @staticmethod
    def insert_person(dict_body):
        # 新增记录
        dict_data = dict_body["insertRecords"]
        print(dict_data)
        for data in dict_data:
            personinfo = PersonInfoSerializer(data=data, partial=True)
            if personinfo.is_valid():
                personinfo.save()
            else:
                for key, value in personinfo.errors.items():
                    print(key, ":", value)
                return Response(status=status.HTTP_400_BAD_REQUEST)
        return Response(status=status.HTTP_200_OK)

    @staticmethod
    def update_person(dict_body):
        dict_data = dict_body["updateRecords"]
        for data in dict_data:
            id = data["id"]
            obj = PersonInfo.objects.get(id=id)
            p = PersonInfoSerializer(instance=obj, data=data)
            if p.is_valid():
                p.save()
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)
        return Response(status=status.HTTP_200_OK)

    @staticmethod
    def remove_person(dict_body):
        # 删除记录
        remove_dict = dict_body["removeRecords"]
        for remove_d in remove_dict:
            remove_obj = PersonInfo.objects.get(id=remove_d["id"])
            remove_obj.delete()
        return Response(status=status.HTTP_200_OK)


class GPersonInfoAll(generics.ListAPIView):
    """导出或打印所有数据时会调用此接口"""
    queryset = PersonInfo.objects.all()
    serializer_class = PersonInfoSerializer


class SexList(APIView):
    def get(self, request):
        data = [{'label': '男', 'value': 1}, {'label': '女', 'value': 0}]
        return Response(data=data, status=status.HTTP_200_OK)

6. urls.py代码

from django.urls import path, re_path
from app01 import views

urlpatterns = [
    re_path("personinfo/list/(\d+)/", views.GPersonInfoList.as_view(), name="person_info_list"),
    path("personinfo/all/", views.GPersonInfoAll.as_view(), name="person_info_all"),
    path("personinfo/save/", views.PersonInfoSave.as_view(), name="person_info_save"),
    path("personinfo/sexlist/", views.SexList.as_view(), name="sexlist"),
]

7. App.vue代码

<template>
  <vxe-grid ref="xGrid" v-bind="gridOptions" style="padding: 0 50px"></vxe-grid>
</template>

<script>
export default {
  data () {
    return {
      gridOptions: {
        border: true,
        resizable: true,
        showHeaderOverflow: true,
        showOverflow: true,
        highlightHoverRow: true,
        keepSource: true,
        id: 'full_edit_1',
        height: 600,
        rowId: 'id',
        customConfig: {
          storage: true,
          checkMethod: this.checkColumnMethod
        },
        printConfig: {
          columns: [
            { field: 'name' },
            { field: 'email' },
            { field: 'nickname' },
            { field: 'age' },
            { field: 'amount' }
          ],
          modes: ['selected', 'current', 'all']
        },
        sortConfig: {
          trigger: 'cell',
          remote: true
        },
        filterConfig: {
          remote: true
        },
        pagerConfig: {
          pageSize: 10,
          pageSizes: [5, 10, 15, 20, 50, 100, 200, 500, 1000]
        },
        formConfig: {
          titleWidth: 100,
          titleAlign: 'right',
          items: [
            { field: 'name', title: '名字', span: 8, titlePrefix: { message: '名称必须填写', icon: 'fa fa-exclamation-circle' }, itemRender: { name: '$input', props: { placeholder: '请输入名称' } } },
            { field: 'email', title: '邮件', span: 8, itemRender: { name: '$input', props: { placeholder: '请输入邮件' } } },
            { field: 'nickname', title: '昵称', span: 8, itemRender: { name: '$input', props: { placeholder: '请输入昵称' } } },
            { field: 'role', title: '角色', span: 8, folding: true, itemRender: { name: '$input', props: { placeholder: '请输入角色' } } },
            { field: 'sex', title: '性别', span: 8, folding: true, titleSuffix: { message: '注意,必填信息!', icon: 'fa fa-info-circle' }, itemRender: { name: '$select', options: [] } },
            { field: 'age', title: '年龄', span: 8, folding: true, itemRender: { name: '$input', props: { type: 'number', min: 1, max: 120, placeholder: '请输入年龄' } } },
            { span: 24, align: 'center', collapseNode: true, itemRender: { name: '$buttons', children: [{ props: { type: 'submit', content: '查询', status: 'primary' } }, { props: { type: 'reset', content: '重置' } }] } }
          ]
        },
        toolbarConfig: {
          buttons: [
            { code: 'insert_actived', name: '新增', icon: 'fa fa-plus' },
            { code: 'delete', name: '直接删除', icon: 'fa fa-trash-o' },
            { code: 'mark_cancel', name: '删除/取消', icon: 'fa fa-trash-o' },
            { code: 'save', name: '保存', icon: 'fa fa-save', status: 'success' }
          ],
          refresh: true,
          // import: true,
          export: true,
          print: true,
          zoom: true,
          custom: true
        },
        proxyConfig: {
          seq: true, // 启用动态序号代理
          sort: true, // 启用排序代理
          filter: true, // 启用筛选代理
          form: true, // 启用表单代理
          props: {
            result: 'results',
            total: 'count'
          },
          ajax: {
            // 接收 Promise 对象
            query: ({page, sorts, filters, form}) => {
              const queryParams = Object.assign({}, form)
              // 处理排序条件
              const firstSort = sorts[0]
              if (firstSort) {
                if (firstSort.order === 'asc') {
                  queryParams.ordering = firstSort.property
                } else {
                  queryParams.ordering = '-' + firstSort.property
                }
              }
              // 处理筛选条件
              filters.forEach(({ property, values }) => {
                queryParams[property] = values.join(',')
              })
              // 显示第几页
              queryParams.page = page.currentPage
              // return XEAjax.get(`https://api.xuliangzhan.com:10443/api/pub/page/list/${page.pageSize}/${page.currentPage}`, queryParams)
              return this.$XEAjax.get(`http://127.0.0.1:8000/app01/personinfo/list/${page.pageSize}/`, queryParams)
            },
            // delete: ({ body }) => XEAjax.post('https://api.xuliangzhan.com:10443/api/pub/save', body),
            // save: ({ body }) => XEAjax.post('https://api.xuliangzhan.com:10443/api/pub/save', body)
            delete: ({ body }) => this.$XEAjax.post(`http://127.0.0.1:8000/app01/personinfo/save/`, body),
            save: ({ body }) => this.$XEAjax.post(`http://127.0.0.1:8000/app01/personinfo/save/`, body),
            // 被某些特殊功能所触发,例如:导出数据 mode=all 时,会触发该方法并对返回的数据进行导出
            queryAll: () => this.$XEAjax.get('http://127.0.0.1:8000/app01/personinfo/all/')
          }
        },
        columns: [
          { type: 'checkbox', title: 'ID', width: 80 },
          { field: 'name', title: 'Name', minWidth: 160, sortable: true, titleHelp: { message: '名称必须填写!' }, editRender: { name: 'input' } },
          {
            field: 'role',
            title: 'Role',
            sortable: true,
            width: 160,
            filters: [
              { label: '前端开发', value: '前端' },
              { label: '后端开发', value: '后端' },
              { label: '测试', value: '测试' },
              { label: '程序员鼓励师', value: '程序员鼓励师' }
            ],
            filterMultiple: false,
            editRender: { name: 'input' }
          },
          { field: 'email', minWidth: 160, title: 'Email', editRender: { name: 'input' } },
          { field: 'nickname', minWidth: 160, title: 'Nickname', editRender: { name: 'input' } },
          { field: 'sex', title: 'Sex', minWidth: 160, editRender: { name: '$select', options: [] } },
          { field: 'age', title: 'Age', visible: false, minWidth: 160, sortable: true, editRender: { name: '$input', props: { type: 'number', min: 1, max: 120 } } },
          { field: 'amount', title: 'Amount', minWidth: 160, formatter: this.formatAmount, editRender: { name: '$input', props: { type: 'float', digits: 2 } } },
          { field: 'updateDate', minWidth: 160, title: 'Update Date', visible: false, sortable: true, formatter: this.formatDate },
          { field: 'createDate', minWidth: 160, title: 'Create Date', visible: false, sortable: true, formatter: this.formatDate }
        ],
        importConfig: {
          remote: true,
          importMethod: this.importMethod,
          types: ['csv', 'xlsx'],
          modes: ['insert']
        },
        exportConfig: {
          // remote: true,
          // exportMethod: this.exportMethod,
          // 默认选中类型
          type: 'csv',
          types: ['csv', 'html', 'xml', 'txt'],
          modes: ['selected', 'current', 'all']
        },
        checkboxConfig: {
          labelField: 'id',
          reserve: true,
          highlight: true,
          range: true
        },
        editRules: {
          name: [
            { required: true, message: '名字' },
            { min: 3, max: 50, message: '名称长度在 3 到 50 个字符' }
          ],
          email: [
            { required: true, message: '邮件必须填写' }
          ],
          role: [
            { required: true, message: '角色必须填写' }
          ]
        },
        editConfig: {
          // 单击编辑
          // trigger: 'click',
          // 双击编辑
          trigger: 'dblclick',
          mode: 'row',
          showStatus: true
        }
      }
    }
  },
  mounted () {
    this.findSexList()
  },
  methods: {
    async findSexList () {
      // const sexList = [{'label': '男', 'value': 1}, {'label': '女', 'value': 0}]
      const sexList = await this.$XEAjax.get('http://127.0.0.1:8000/app01/personinfo/sexlist/')
      // 异步更新下拉选项
      this.sexList = sexList
      const xGrid = this.$refs.xGrid
      if (xGrid) {
        const sexColumn = xGrid.getColumnByField('sex')
        sexColumn.editRender.options = sexList
        const sexItem = xGrid.getFormItems(4)
        sexItem.itemRender.options = sexList
      }
    },
    formatAmount ({ cellValue }) {
      // return cellValue ? `$${XEUtils.commafy(XEUtils.toNumber(cellValue), { digits: 2 })}` : ''
      return cellValue ? `$${this.$XEUtils.commafy(this.$XEUtils.toNumber(cellValue), { digits: 2 })}` : ''
    },
    formatDate ({ cellValue }) {
      if (cellValue) {
        const date1 = cellValue.split('T')[0]
        const date2 = cellValue.split('T')[1].split('.')[0]
        const date3 = date1 + ' ' + date2
        // return this.$XEUtils.toDateString(cellValue, 'yyyy-MM-dd HH:mm:ss')
        return date3
      }
    },
    checkColumnMethod ({ column }) {
      if (['nickname', 'role'].includes(column.property)) {
        return false
      }
      return true
    }
  }
}
</script>

8. main.js代码

import Vue from 'vue'
import App from './App'
// import router from './router'
import axios from 'axios'
import qs from 'qs'
import VueResource from 'vue-resource'
import XEUtils from 'xe-utils'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
import XEAjax from 'xe-ajax'

// 全局注册
Vue.config.productionTip = false
Vue.prototype.$axios = axios
Vue.prototype.$qs = qs
Vue.prototype.$XEAjax = XEAjax
Vue.prototype.$XEUtils = XEUtils
Vue.use(VueResource)
Vue.use(VXETable)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  // router,
  components: { App },
  template: '<App/>'
})

猜你喜欢

转载自blog.csdn.net/weixin_42289273/article/details/113753187
今日推荐