task rate

This commit is contained in:
caoqianming 2021-03-17 09:23:55 +08:00
parent ec2499a147
commit 7d53e69be0
12 changed files with 180 additions and 48 deletions

View File

@ -5,6 +5,9 @@
<breadcrumb class="breadcrumb-container" /> <breadcrumb class="breadcrumb-container" />
<div class="right-menu"> <div class="right-menu">
<div class="right-menu-item" >
{{name}}
</div>
<el-dropdown class="avatar-container" trigger="click"> <el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar"> <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
@ -40,7 +43,6 @@
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
export default { export default {
components: { components: {
Breadcrumb, Breadcrumb,
@ -49,7 +51,8 @@ export default {
computed: { computed: {
...mapGetters([ ...mapGetters([
'sidebar', 'sidebar',
'avatar' 'avatar',
'name'
]) ])
}, },
methods: { methods: {

View File

@ -112,13 +112,13 @@ export const asyncRoutes = [
component: Layout, component: Layout,
redirect: '/supervision/task', redirect: '/supervision/task',
name:'Supervision', name:'Supervision',
meta: { title: '日常监督', icon: 'table', perms: ['supervision_manage'] }, meta: { title: '日常监督', icon: 'table', perms: ['supervision'] },
children: [ children: [
{ {
path: 'content', path: 'content',
name: 'Content', name: 'Content',
component: () => import('@/views/supervision/content.vue'), component: () => import('@/views/supervision/content.vue'),
meta: { title: '材料清单', icon: 'documentation', perms: ['content_manage'] } meta: { title: '材料清单', icon: 'documentation', perms: ['content'] }
}, },
{ {
path: 'task', path: 'task',

View File

@ -100,7 +100,7 @@
</el-table-column> </el-table-column>
<el-table-column label="上报单位"> <el-table-column label="上报单位">
<template slot-scope="scope">{{ scope.row.dept_.name }}</template> <template slot-scope="scope">{{ scope.row.belong_dept_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="记录状态"> <el-table-column label="记录状态">
<template slot-scope="scope">{{ scope.row.state }}</template> <template slot-scope="scope">{{ scope.row.state }}</template>

View File

@ -23,7 +23,7 @@
v-model="record.note" v-model="record.note"
placeholder="" placeholder=""
type="textarea" type="textarea"
:readonly="record.state != '待上报'" :readonly="data.action!= 'update'"
> >
</el-input> </el-input>
</div> </div>
@ -41,13 +41,13 @@
v-model="record.noteb" v-model="record.noteb"
placeholder="" placeholder=""
type="textarea" type="textarea"
:readonly="record.state == '已上报' && record.state == '已确认'" :readonly="data.action!='up'"
> >
</el-input> </el-input>
</div> </div>
<div class="ma"> <div class="ma">
<span class="term">是否适用</span> <span class="term">是否适用</span>
<el-switch v-model="record.is_yes"></el-switch> <el-switch v-model="record.is_yes" :disabled="data.action=='view'"></el-switch>
</div> </div>
<div class="ma"> <div class="ma">
<el-upload <el-upload
@ -61,7 +61,7 @@
:file-list="fileList" :file-list="fileList"
accept="image/*,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" accept="image/*,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
> >
<el-button size="small" type="primary">上传文件</el-button> <el-button size="small" type="primary" :disabled="data.action!='up'">上传文件</el-button>
<template #tip> <template #tip>
<div class="el-upload__tip"> <div class="el-upload__tip">
可上传pdf,word,ppt,excel,图片文件,大小不超过20M 可上传pdf,word,ppt,excel,图片文件,大小不超过20M
@ -72,7 +72,7 @@
<el-divider></el-divider> <el-divider></el-divider>
<div style="text-align: right"> <div style="text-align: right">
<el-button type="primary" @click="confirm()" v-if="this.data.action =='reject'">驳回</el-button> <el-button type="primary" @click="confirm()" v-if="this.data.action =='reject'">驳回</el-button>
<el-button type="primary" @click="confirm()" >确认</el-button> <el-button type="primary" @click="confirm()" v-else>确认</el-button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -147,6 +147,12 @@
@click="handleReject(scope)" @click="handleReject(scope)"
>驳回</el-button >驳回</el-button
> >
<el-button
:disabled="!checkPermission(['record_view'])"
size="small"
@click="handleView(scope)"
>查看</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -180,7 +186,7 @@
当前选择的是 当前选择的是
<span style="color: red">{{ now.name }}</span> <span style="color: red">{{ now.name }}</span>
</div> </div>
<el-table <el-table
v-loading="listLoading" v-loading="listLoading"
:data="recordList" :data="recordList"
border border
@ -189,11 +195,61 @@
highlight-current-row highlight-current-row
max-height="600px" max-height="600px"
> >
<el-table-column label="名称"> <el-table-column label="序号" type="index" align="center" />
<el-table-column label="材料">
<template slot-scope="scope">{{
scope.row.content_.name
}}</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">{{ <template slot-scope="scope">{{
scope.row.state scope.row.state
}}</template> }}</template>
</el-table-column> </el-table-column>
<el-table-column label="上报情况">
<template slot-scope="scope"
>{{ scope.row.up_user }}/{{ scope.row.up_date }}</template
>
</el-table-column>
<el-table-column label="文件">
<template slot-scope="scope">
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-button
v-if="scope.row.state == '待上报'"
type="primary"
:disabled="!checkPermission(['record_update'])"
size="small"
@click="handleUpdate(scope)"
>编辑</el-button
>
<el-button
v-if="scope.row.state == '已提交'"
type="primary"
:disabled="!checkPermission(['record_confirm'])"
size="small"
@click="handleConfirm(scope)"
>确认</el-button
>
<el-button
v-if="scope.state == '已提交'"
:disabled="!checkPermission(['record_reject'])"
type="danger"
size="small"
@click="handleReject(scope)"
>驳回</el-button
>
<el-button
:disabled="!checkPermission(['record_view'])"
size="small"
@click="handleView(scope)"
>查看</el-button
>
</template>
</el-table-column>
</el-table> </el-table>
</el-card> </el-card>
</el-col> </el-col>
@ -346,7 +402,18 @@ export default {
this.drawer = true; this.drawer = true;
}, },
handleConfirm(scope) { handleConfirm(scope) {
this.data = {
action: "reject",
record: scope.row,
};
this.drawer = true;
},
handleView(scope) {
this.data = {
action: "view",
record: scope.row,
};
this.drawer = true;
}, },
handleDo(data){ handleDo(data){
this.drawer = false this.drawer = false

View File

@ -1,5 +1,6 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework.mixins import ListModelMixin from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from .models import * from .models import *
from .serializers import * from .serializers import *

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.5 on 2021-03-17 01:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supervision', '0008_auto_20210316_0827'),
]
operations = [
migrations.RemoveField(
model_name='task',
name='complete_rate',
),
migrations.AddField(
model_name='task',
name='confirm_rate',
field=models.IntegerField(default=0, verbose_name='确认率'),
),
migrations.AddField(
model_name='task',
name='up_rate',
field=models.IntegerField(default=0, verbose_name='上报率'),
),
]

View File

@ -29,7 +29,8 @@ class Task(CommonBModel):
name = models.CharField('名称', max_length=100) name = models.CharField('名称', max_length=100)
end_date = models.DateField('截止时间', null=True, blank=True) end_date = models.DateField('截止时间', null=True, blank=True)
note = models.TextField('任务备注', null=True, blank=True) note = models.TextField('任务备注', null=True, blank=True)
complete_rate = models.IntegerField('完成度', default=0) up_rate = models.IntegerField('上报率', default=0)
confirm_rate = models.IntegerField('确认率', default=0)
contents = models.ManyToManyField('supervision.content', through='supervision.record') contents = models.ManyToManyField('supervision.content', through='supervision.record')
depts = models.ManyToManyField('system.organization', through='supervision.record') depts = models.ManyToManyField('system.organization', through='supervision.record')
state = models.CharField('任务状态', max_length=50, choices=state_choices, default='创建中') state = models.CharField('任务状态', max_length=50, choices=state_choices, default='创建中')

View File

@ -35,7 +35,7 @@ class TaskCreateUpdateSerializer(serializers.ModelSerializer):
class TaskListSerializer(serializers.ModelSerializer): class TaskListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Task model = Task
fields = ['name', 'end_date', 'note', 'complete_rate', 'state', 'id'] fields = ['name', 'end_date', 'note', 'up_rate', 'state', 'id', 'confirm_rate']
class RecordSerializer(serializers.ModelSerializer): class RecordSerializer(serializers.ModelSerializer):
task_ = TaskListSerializer(source='task') task_ = TaskListSerializer(source='task')

View File

@ -10,11 +10,13 @@ from rest_framework.response import Response
from apps.system.models import Organization from apps.system.models import Organization
from django.db.models import Count from django.db.models import Count
from utils.pagination import PageOrNot from utils.pagination import PageOrNot
from apps.system.mixins import OptimizationMixin
from apps.system.mixins import CreateUpdateCustomMixin from apps.system.mixins import CreateUpdateCustomMixin
from apps.system.permission_data import RbacFilterSet
from django.utils import timezone from django.utils import timezone
from .filters import RecordFilter from .filters import RecordFilter
from django.db.models.query import QuerySet
from django.core.cache import cache
from apps.system.permission import get_permission_list
# Create your views here. # Create your views here.
class ContentViewSet(CreateUpdateCustomMixin, ModelViewSet): class ContentViewSet(CreateUpdateCustomMixin, ModelViewSet):
@ -97,7 +99,20 @@ class TaskViewSet(CreateUpdateCustomMixin, ModelViewSet):
serializer = OrganizationSimpleSerializer(obj.depts.distinct(), many=True) serializer = OrganizationSimpleSerializer(obj.depts.distinct(), many=True)
return Response(serializer.data) return Response(serializer.data)
class RecordViewSet(OptimizationMixin,PageOrNot, CreateUpdateCustomMixin, ModelViewSet): def cal_task_rate(task):
"""
计算任务上报率, 确认率
"""
objs = Record.objects.filter(task=task)
if objs.exists():
count = objs.count()
up_rate = objs.filter(state__in=['已确认','已上报']).count()/count
confirm_rate = objs.filter(state='已确认').count()/count
task.up_rate = int(up_rate)
task.confirm_rate = int(confirm_rate)
task.save()
class RecordViewSet(RbacFilterSet, PageOrNot, CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'record_create', perms_map = {'get': '*', 'post': 'record_create',
'put': 'record_update', 'delete': 'record_delete'} 'put': 'record_update', 'delete': 'record_delete'}
queryset = Record.objects.all() queryset = Record.objects.all()

View File

@ -18,8 +18,7 @@ def get_permission_list(user):
perms = perms | i.perms.all() perms = perms | i.perms.all()
perms_list = perms.values_list('method', flat=True) perms_list = perms.values_list('method', flat=True)
perms_list = list(set(perms_list)) perms_list = list(set(perms_list))
cache.set(user.username, perms_list) cache.set(user.username + '__perms', perms_list)
# cache.persist(user.username)
return perms_list return perms_list
@ -35,7 +34,10 @@ class RbacPermission(BasePermission):
:param view: :param view:
:return: :return:
""" """
perms = cache.get(request.user) if not request.user:
perms = ['visitor'] # 如果没有经过认证,视为游客
else:
perms = cache.get(request.user.username + '__perms')
if not perms: if not perms:
perms = get_permission_list(request.user) perms = get_permission_list(request.user)
if perms: if perms:
@ -59,7 +61,10 @@ class RbacPermission(BasePermission):
""" """
Return `True` if permission is granted, `False` otherwise. Return `True` if permission is granted, `False` otherwise.
""" """
has_obj_perm(request.user, obj) # if not request.user:
# return False
# if hasattr(obj, 'belong_dept'):
# has_obj_perm(request.user, obj)
return True return True
def has_obj_perm(user, obj): def has_obj_perm(user, obj):
@ -78,8 +83,8 @@ def has_obj_perm(user, obj):
if obj.belong_dept not in roles.depts: if obj.belong_dept not in roles.depts:
return False return False
elif '同级及以下' in data_range: elif '同级及以下' in data_range:
if user.dept.pid: if user.dept.parent:
belong_depts = get_child_queryset2(user.dept.pid) belong_depts = get_child_queryset2(user.dept.parent)
if obj.belong_dept not in belong_depts: if obj.belong_dept not in belong_depts:
return False return False
elif '本级及以下' in data_range: elif '本级及以下' in data_range:

View File

@ -1,15 +1,18 @@
from django.db.models import Q from django.db.models import Q
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from apps.system.mixins import CreateUpdateModelBMixin
from utils.queryset import get_child_queryset2 from utils.queryset import get_child_queryset2
class RbacFilterSet(GenericAPIView): class RbacFilterSet(object):
""" """
数据权限控权返回的queryset 数据权限控权返回的queryset
在必须的View下继承 在必须的View下继承
需要控数据权限的表需有belong_dept, create_by, update_by字段(部门, 创建人, 编辑人) 需要控数据权限的表需有belong_dept, create_by, update_by字段(部门, 创建人, 编辑人)
带性能优化
此处对性能有较大影响,根据业务需求进行修改或取舍
""" """
def get_queryset(self): def get_queryset(self):
assert self.queryset is not None, ( assert self.queryset is not None, (
@ -22,6 +25,13 @@ class RbacFilterSet(GenericAPIView):
if isinstance(queryset, QuerySet): if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request. # Ensure queryset is re-evaluated on each request.
queryset = queryset.all() queryset = queryset.all()
if hasattr(self.get_serializer_class(), 'setup_eager_loading'):
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化
if self.request.user.is_superuser:
return queryset
if hasattr(queryset.model, 'belong_dept'): if hasattr(queryset.model, 'belong_dept'):
user = self.request.user user = self.request.user
roles = user.roles roles = user.roles
@ -33,8 +43,8 @@ class RbacFilterSet(GenericAPIView):
queryset = queryset.filter(belong_dept__in = roles.depts) queryset = queryset.filter(belong_dept__in = roles.depts)
return queryset return queryset
elif '同级及以下' in data_range: elif '同级及以下' in data_range:
if user.dept.pid: if user.dept.parent:
belong_depts = get_child_queryset2(user.dept.pid) belong_depts = get_child_queryset2(user.dept.parent)
queryset = queryset.filter(belong_dept__in = belong_depts) queryset = queryset.filter(belong_dept__in = belong_depts)
return queryset return queryset
elif '本级及以下' in data_range: elif '本级及以下' in data_range:
@ -47,7 +57,6 @@ class RbacFilterSet(GenericAPIView):
elif '仅本人' in data_range: elif '仅本人' in data_range:
queryset = queryset.filter(Q(create_by=user)|Q(update_by=user)) queryset = queryset.filter(Q(create_by=user)|Q(update_by=user))
return queryset return queryset
return queryset return queryset
@ -57,28 +66,32 @@ def rbac_filter_queryset(user, queryset):
需要控数据权限的表需有belong_dept, create_by, update_by字段(部门, 创建人, 编辑人) 需要控数据权限的表需有belong_dept, create_by, update_by字段(部门, 创建人, 编辑人)
传入user实例,queryset 传入user实例,queryset
""" """
if user.is_superuser:
return queryset
roles = user.roles roles = user.roles
data_range = roles.values_list('datas', flat=True) data_range = roles.values_list('datas', flat=True)
if '全部' in data_range: if hasattr(queryset.model, 'belong_dept'):
return queryset if '全部' in data_range:
elif '自定义' in data_range:
if roles.depts.exists():
queryset = queryset.filter(belong_dept__in = roles.depts)
return queryset return queryset
elif '同级及以下' in data_range: elif '自定义' in data_range:
if user.dept.pid: if roles.depts.exists():
belong_depts = get_child_queryset2(user.dept.pid) queryset = queryset.filter(belong_dept__in = roles.depts)
return queryset
elif '同级及以下' in data_range:
if user.dept.parent:
belong_depts = get_child_queryset2(user.dept.parent)
queryset = queryset.filter(belong_dept__in = belong_depts)
return queryset
elif '本级及以下' in data_range:
belong_depts = get_child_queryset2(user.dept)
queryset = queryset.filter(belong_dept__in = belong_depts) queryset = queryset.filter(belong_dept__in = belong_depts)
return queryset return queryset
elif '本级及以下' in data_range: elif '本级' in data_range:
belong_depts = get_child_queryset2(user.dept) queryset = queryset.filter(belong_dept = user.dept)
queryset = queryset.filter(belong_dept__in = belong_depts) return queryset
return queryset elif '仅本人' in data_range:
elif '本级' in data_range: queryset = queryset.filter(Q(create_by=user)|Q(update_by=user))
queryset = queryset.filter(belong_dept = user.dept) return queryset
return queryset
elif '仅本人' in data_range:
queryset = queryset.filter(Q(create_by=user)|Q(update_by=user))
return queryset
return queryset return queryset