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/>'
})
标签:vxe,max,blank,改查,models,length,分离式,null,True 来源: https://blog.csdn.net/weixin_42289273/article/details/113753187