This commit is contained in:
caoqianming 2020-05-27 18:28:10 +08:00
parent d9b700dfe0
commit e85bfe3f08
16 changed files with 288 additions and 51 deletions

View File

@ -1,3 +1,9 @@
import { getToken } from "@/utils/auth";
export function uploadUrl() { export function uploadUrl() {
return process.env.VUE_APP_BASE_API + '/upload/' return process.env.VUE_APP_BASE_API + '/file/'
}
export function upHeaders() {
return { Authorization: "Bearer " + getToken() }
} }

View File

@ -124,7 +124,7 @@ export const asyncRoutes = [
] ]
}, },
{ {
path: 'external-link', path: 'external-link2',
component: Layout, component: Layout,
children: [ children: [
{ {

View File

@ -45,7 +45,7 @@ service.interceptors.response.use(
const res = response.data const res = response.data
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 401) { if (res.code === 401) {
MessageBox.confirm('您已退出,您可以停留在本页或者重新登陆.', '确认退出', { MessageBox.confirm('认证失败,请重新登陆.', '确认退出', {
confirmButtonText: '重新登陆', confirmButtonText: '重新登陆',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'

View File

@ -18,7 +18,7 @@
@node-click="handleOrgClick" @node-click="handleOrgClick"
/> />
</el-col> </el-col>
<el-col :span="18"> <el-col :md="18">
<div> <div>
<el-select <el-select
v-model="listQuery.is_active" v-model="listQuery.is_active"
@ -158,7 +158,7 @@
:show-file-list="false" :show-file-list="false"
:on-success="handleAvatarSuccess" :on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload" :before-upload="beforeAvatarUpload"
:headers="myHeaders" :headers="upHeaders"
> >
<img v-if="user.avatar" :src="user.avatar" class="avatar" /> <img v-if="user.avatar" :src="user.avatar" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" /> <i v-else class="el-icon-plus avatar-uploader-icon" />
@ -199,13 +199,12 @@
</style> </style>
<script> <script>
import { getUserList, createUser, deleteUser, updateUser } from "@/api/user"; import { getUserList, createUser, deleteUser, updateUser } from "@/api/user";
import { getOrgAll } from "@/api/org"; import { getOrgAll } from "@/api/org"
import { getRoleAll } from "@/api/role"; import { getRoleAll } from "@/api/role"
import { genTree } from "@/utils"; import { genTree } from "@/utils"
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission"
import { uploadUrl } from "@/api/file"; import { uploadUrl, upHeaders } from "@/api/file"
import { getToken } from "@/utils/auth"; import Pagination from "@/components/Pagination" // secondary package based on el-pagination
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Treeselect from '@riophae/vue-treeselect' import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css' import '@riophae/vue-treeselect/dist/vue-treeselect.css'
const defaultUser = { const defaultUser = {
@ -219,14 +218,8 @@ export default {
components: { Pagination, Treeselect }, components: { Pagination, Treeselect },
data() { data() {
return { return {
user: { user: defaultUser,
id: "", upHeaders: upHeaders(),
name: "",
username: "",
dept: null,
avatar: ""
},
myHeaders: { Authorization: "Bearer " + getToken() },
uploadUrl: uploadUrl(), uploadUrl: uploadUrl(),
userList: {count:0}, userList: {count:0},
roles: [], roles: [],
@ -267,10 +260,10 @@ export default {
methods: { methods: {
checkPermission, checkPermission,
handleAvatarSuccess(res, file) { handleAvatarSuccess(res, file) {
if (res.code === 200) { if (res.code >= 200) {
this.user.avatar = res.data.path; this.user.avatar = res.data.path
} else { } else {
this.$message.error("头像上传失败!"); this.$message.error("头像上传失败!")
} }
}, },
beforeAvatarUpload(file) { beforeAvatarUpload(file) {

View File

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin from simple_history.admin import SimpleHistoryAdmin
from .models import User, Organization, Role, Permission, DictType, Dict from .models import User, Organization, Role, Permission, DictType, Dict, File
# Register your models here. # Register your models here.
admin.site.register(User) admin.site.register(User)
admin.site.register(Organization) admin.site.register(Organization)
@ -8,3 +8,4 @@ admin.site.register(Role)
admin.site.register(Permission) admin.site.register(Permission)
admin.site.register(DictType) admin.site.register(DictType)
admin.site.register(Dict, SimpleHistoryAdmin) admin.site.register(Dict, SimpleHistoryAdmin)
admin.site.register(File)

View File

@ -0,0 +1,35 @@
# Generated by Django 3.0.5 on 2020-05-27 03:47
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('system', '0005_historicaldict'),
]
operations = [
migrations.CreateModel(
name='File',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(blank=True, max_length=30, null=True, verbose_name='名称')),
('size', models.IntegerField(blank=True, default=1, null=True, verbose_name='文件大小')),
('path', models.CharField(blank=True, max_length=1000, null=True, verbose_name='存储地址')),
('file', models.FileField(upload_to='')),
],
options={
'abstract': False,
},
),
migrations.AlterField(
model_name='role',
name='datas',
field=models.CharField(choices=[('全部', '全部'), ('自定义', '自定义'), ('同级及以下', '同级及以下'), ('本级及以下', '本级及以下'), ('本级', '本级'), ('仅本人', '仅本人')], default='本级及以下', max_length=50, verbose_name='数据权限'),
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 3.0.5 on 2020-05-27 07:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0006_auto_20200527_1147'),
]
operations = [
migrations.AddField(
model_name='file',
name='create_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
),
migrations.AddField(
model_name='file',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AlterField(
model_name='file',
name='file',
field=models.FileField(upload_to='%Y/%m/%d/'),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.5 on 2020-05-27 08:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0007_auto_20200527_1508'),
]
operations = [
migrations.RemoveField(
model_name='file',
name='path',
),
migrations.AddField(
model_name='file',
name='type',
field=models.CharField(choices=[('文档', '文档'), ('视频', '视频'), ('音频', '音频'), ('图片', '图片'), ('其它', '其它')], default='文档', max_length=50, verbose_name='文件类型'),
),
migrations.AlterField(
model_name='file',
name='file',
field=models.FileField(upload_to='%Y/%m/%d/', verbose_name='文件'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.5 on 2020-05-27 09:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0008_auto_20200527_1641'),
]
operations = [
migrations.AddField(
model_name='file',
name='mime',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='文件格式'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.5 on 2020-05-27 09:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0009_file_mime'),
]
operations = [
migrations.AddField(
model_name='file',
name='path',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='地址'),
),
]

View File

@ -0,0 +1,31 @@
from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings
class CreateModelAMixin:
"""
业务用基本表A用
"""
def perform_create(self, serializer):
serializer.save(create_by = self.request.user)
class UpdateModelAMixin:
"""
业务用基本表A用
"""
def perform_update(self, serializer):
serializer.save(update_by = self.request.user)
class CreateModelBMixin:
"""
业务用基本表B用
"""
def perform_create(self, serializer):
serializer.save(create_by = self.request.user, belong_to=self.request.user.dept)
class UpdateModelBMixin:
"""
业务用基本表B用
"""
def perform_update(self, serializer):
serializer.save(update_by = self.request.user)

View File

@ -164,9 +164,10 @@ class Dict(SoftModel):
def __str__(self): def __str__(self):
return self.name return self.name
class CommonModel(SoftModel):
class CommonAModel(SoftModel):
""" """
业务用基本表 业务用基本表A,包含create_by, update_by字段
""" """
create_by = models.ForeignKey( create_by = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='create_by') User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='create_by')
@ -175,3 +176,33 @@ class CommonModel(SoftModel):
class Meta: class Meta:
abstract = True abstract = True
class CommonBModel(SoftModel):
"""
业务用基本表B,包含create_by, update_by, belong_to字段
"""
create_by = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='create_by')
update_by = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='update_by')
belong_to = models.ForeignKey(
Organization, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name='belong_to')
class Meta:
abstract = True
class File(CommonAModel):
name = models.CharField('名称', max_length=30, null=True, blank=True)
size = models.IntegerField('文件大小', default=1, null=True, blank=True)
file = models.FileField('文件', upload_to='%Y/%m/%d/')
type_choices = (
('文档', '文档'),
('视频', '视频'),
('音频', '音频'),
('图片', '图片'),
('其它', '其它')
)
mime = models.CharField('文件格式', max_length=50, null=True, blank=True)
type = models.CharField('文件类型', max_length=50, choices=type_choices, default='文档')
path = models.CharField('地址', max_length=1000, null=True, blank=True)

View File

@ -2,8 +2,12 @@ import re
from rest_framework import serializers from rest_framework import serializers
from .models import Organization, Permission, Role, User, Position, DictType, Dict from .models import Organization, Permission, Role, User, Position, DictType, Dict, File
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = "__all__"
class DictTypeSerializer(serializers.ModelSerializer): class DictTypeSerializer(serializers.ModelSerializer):
""" """
@ -71,7 +75,7 @@ class UserListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ('id', 'name', 'phone', 'email', 'position', fields = ('id', 'name', 'phone', 'email', 'position',
'username', 'is_active', 'date_joined', 'dept_name', 'dept', 'roles') 'username', 'is_active', 'date_joined', 'dept_name', 'dept', 'roles', 'avatar')
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):

View File

@ -20,14 +20,14 @@ from utils.queryset import get_child_queryset2
from .filters import UserFilter from .filters import UserFilter
from .models import (Dict, DictType, Organization, Permission, Position, Role, from .models import (Dict, DictType, Organization, Permission, Position, Role,
User) User, File)
from .permission import RbacPermission, get_permission_list from .permission import RbacPermission, get_permission_list
from .permission_data import RbacFilterSet from .permission_data import RbacFilterSet
from .serializers import (DictSerializer, DictTypeSerializer, from .serializers import (DictSerializer, DictTypeSerializer,
OrganizationSerializer, PermissionSerializer, OrganizationSerializer, PermissionSerializer,
PositionSerializer, RoleSerializer, PositionSerializer, RoleSerializer,
UserCreateSerializer, UserListSerializer, UserCreateSerializer, UserListSerializer,
UserModifySerializer) UserModifySerializer, FileSerializer)
logger = logging.getLogger('log') logger = logging.getLogger('log')
# logger.info('请求成功! response_code:{}response_headers:{}response_body:{}'.format(response_code, response_headers, response_body[:251])) # logger.info('请求成功! response_code:{}response_headers:{}response_body:{}'.format(response_code, response_headers, response_body[:251]))
@ -144,7 +144,8 @@ class UserViewSet(ModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = self.queryset queryset = self.queryset
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 if hasattr(self.get_serializer_class(), 'setup_eager_loading'):
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化
dept = self.request.query_params.get('dept', None) # 该部门及其子部门所有员工 dept = self.request.query_params.get('dept', None) # 该部门及其子部门所有员工
if dept is not None: if dept is not None:
deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept)) deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept))
@ -209,3 +210,36 @@ class UserViewSet(ModelViewSet):
'perms': perms, 'perms': perms,
} }
return Response(data) return Response(data)
from rest_framework.parsers import MultiPartParser, JSONParser, FileUploadParser
from .mixins import CreateModelAMixin
from django.conf import settings
class FileViewSet(ModelViewSet):
"""
文件增删改查
"""
perms_map = None
parser_classes = [MultiPartParser, JSONParser]
queryset = File.objects.all()
serializer_class = FileSerializer
search_fields = ['name']
ordering = '-create_time'
def perform_create(self, serializer):
fileobj = self.request.data.get('file')
name = fileobj._name
size = fileobj.size
mime = fileobj.content_type
type = '其它'
if 'image' in mime:
type = '图片'
elif 'video' in mime:
type = '视频'
elif 'audio' in mime:
type = '音频'
elif 'application' or 'text' in mime:
type = '文档'
instance = serializer.save(create_by = self.request.user, name=name, size=size, type=type, mime=mime)
instance.path = settings.MEDIA_URL + instance.file.name
instance.save()

View File

@ -24,6 +24,12 @@ from django.conf import settings
from apps.system.views import LogoutView from apps.system.views import LogoutView
from utils.view import UploadFileView from utils.view import UploadFileView
from rest_framework import routers
from apps.system.views import FileViewSet
router = routers.DefaultRouter()
router.register('file', FileViewSet, basename="file")
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('upload/', UploadFileView.as_view(), name='file_upload'), path('upload/', UploadFileView.as_view(), name='file_upload'),
@ -33,4 +39,7 @@ urlpatterns = [
path('system/', include('apps.system.urls')), path('system/', include('apps.system.urls')),
path('docs/', include_docs_urls(title="接口文档", path('docs/', include_docs_urls(title="接口文档",
authentication_classes=[], permission_classes=[])), authentication_classes=[], permission_classes=[])),
path('', include(router.urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -2,23 +2,23 @@ from django.db import models
from django.apps import apps from django.apps import apps
def get_child_queryset_u(checkQueryset, fatherQueryset, noneQueryset, hasParent=True): def get_child_queryset_u(checkQueryset, obj, hasParent=True):
''' '''
获取所有子集 获取所有子集
查checkQueryset 查的范围checkQueryset
父fatherQueryset 父obj
空noneQueryset
是否包含父默认True 是否包含父默认True
''' '''
if fatherQueryset is None: cls = type(obj)
return noneQueryset queryset = cls.objects.none()
fatherQueryset = cls.objects.filter(pk=obj.id)
if hasParent: if hasParent:
noneQueryset = noneQueryset | fatherQueryset queryset = queryset | fatherQueryset
child_queryset = checkQueryset.filter(pid=fatherQueryset.first()) child_queryset = checkQueryset.filter(pid=obj)
while child_queryset: while child_queryset:
noneQueryset = noneQueryset | child_queryset queryset = queryset | child_queryset
child_queryset = checkQueryset.filter(pid__in=child_queryset) child_queryset = checkQueryset.filter(pid__in=child_queryset)
return noneQueryset.distinct() return queryset
def get_child_queryset(name, pk, hasParent=True): def get_child_queryset(name, pk, hasParent=True):
@ -44,17 +44,17 @@ def get_child_queryset(name, pk, hasParent=True):
def get_child_queryset2(obj, hasParent=True): def get_child_queryset2(obj, hasParent=True):
''' '''
获取所有子集 获取所有子集
app.model实例 obj实例
数据表需包含pid字段
是否包含父默认True 是否包含父默认True
''' '''
cls = type(obj) cls = type(obj)
queryset = cls.objects.none() queryset = cls.objects.none()
fatherQueryset = cls.objects.filter(pk=obj.id) fatherQueryset = cls.objects.filter(pk=obj.id)
if fatherQueryset.exists(): if hasParent:
if hasParent: queryset = queryset | fatherQueryset
queryset = queryset | fatherQueryset child_queryset = cls.objects.filter(pid=obj)
child_queryset = cls.objects.filter(pid=fatherQueryset.first()) while child_queryset:
while child_queryset: queryset = queryset | child_queryset
queryset = queryset | child_queryset child_queryset = cls.objects.filter(pid__in=child_queryset)
child_queryset = cls.objects.filter(pid__in=child_queryset)
return queryset return queryset