From c6481027bf68b7071c6705b91ed11f49b28c9974 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 15 Oct 2021 10:17:18 +0800 Subject: [PATCH 01/31] =?UTF-8?q?=E5=8A=A0=E7=AD=BE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0004_subproductionplan_steps.py | 18 ++++++++++++++++++ hb_server/apps/pm/models.py | 2 +- hb_server/apps/pm/views.py | 6 +++++- hb_server/apps/wf/serializers.py | 2 +- hb_server/apps/wf/views.py | 2 +- 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 hb_server/apps/pm/migrations/0004_subproductionplan_steps.py diff --git a/hb_server/apps/pm/migrations/0004_subproductionplan_steps.py b/hb_server/apps/pm/migrations/0004_subproductionplan_steps.py new file mode 100644 index 0000000..56b78dd --- /dev/null +++ b/hb_server/apps/pm/migrations/0004_subproductionplan_steps.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-10-15 02:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0003_auto_20211014_1503'), + ] + + operations = [ + migrations.AddField( + model_name='subproductionplan', + name='steps', + field=models.JSONField(default=list, verbose_name='工艺步骤'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index f7db0c1..5aab5f3 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -37,7 +37,7 @@ class SubProductionPlan(CommonAModel): end_date = models.DateField('计划完工日期') workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) - # steps = models.JSONField('工艺步骤', default=list) + steps = models.JSONField('工艺步骤', default=list) class Meta: verbose_name = '子生产计划' verbose_name_plural = verbose_name diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index ca26e45..b74e05c 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -13,6 +13,7 @@ from apps.sam.models import Order from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework.decorators import action +from django.db.models import F # Create your views here. def updateOrderPlanedCount(order): @@ -69,9 +70,12 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel raise APIException('已生成子计划') subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number') for i in subps: + steps = UsedStep.objects.filter(subproduction=i, is_deleted=False, subproduction__is_deleted=False, step__is_deleted=False)\ + .order_by('step__number').annotate(id=F('step__id'), number=F('step__number'), name=F('step__name')).values('id', 'number', 'name', 'remark') SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, start_date=production_plan.start_date, end_date=production_plan.end_date, - workshop=i.process.workshop, process=i.process, create_by=request.user) + workshop=i.process.workshop, process=i.process, create_by=request.user, + steps = list(steps)) production_plan.is_planed=True production_plan.save() return Response() diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index b9256bd..2029ec8 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -136,7 +136,7 @@ class TicketCloseSerializer(serializers.Serializer): class TicketAddNodeSerializer(serializers.Serializer): suggestion = serializers.CharField(label="加签说明", required = False) - add_node_man = serializers.IntegerField(label='加签人') + toadd_user = serializers.IntegerField(label='发送给谁去加签') class TicketAddNodeEndSerializer(serializers.Serializer): suggestion = serializers.CharField(label="加签意见", required = False) \ No newline at end of file diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index ddc9bc2..a96006c 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -321,7 +321,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin """ ticket = self.get_object() data = request.data - add_user = User.objects.get(pk=data['add_node_man']) + add_user = User.objects.get(pk=data['toadd_user']) ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL ticket.participant = add_user.id ticket.in_add_node = True From fbee94806b0cb3e1f2441de31e0dff4f063992d5 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 15 Oct 2021 10:36:46 +0800 Subject: [PATCH 02/31] =?UTF-8?q?=E7=94=9F=E6=88=90=E5=AD=90=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E5=B7=A5=E5=BA=8F=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/models.py | 2 +- hb_server/apps/pm/serializers.py | 3 +++ hb_server/apps/pm/views.py | 11 ++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 62f983e..7675957 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -174,7 +174,7 @@ class UsedStep(CommonAModel): """ 涉及的生产子工序 """ - step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedsteps') + step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedstep') remark = models.TextField('生产备注', null=True, blank=True) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index bc8f6b4..1950bce 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -35,3 +35,6 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): class Meta: model = SubProductionPlan fields = ['start_date', 'end_date'] + +class GenSubPlanSerializer(serializers.Serializer): + pass diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index b74e05c..a9856cd 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -4,7 +4,7 @@ from apps.em.models import Equipment from apps.em.serializers import EquipmentSerializer from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep from apps.system.mixins import CreateUpdateModelAMixin -from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer +from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin from apps.pm.models import ProductionPlan, SubProductionPlan from rest_framework.viewsets import GenericViewSet, ModelViewSet @@ -60,7 +60,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel updateOrderPlanedCount(instance.order) return Response() - @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) + @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer) def gen_subplan(self, request, pk=None): """ 生成子计划 @@ -70,8 +70,8 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel raise APIException('已生成子计划') subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number') for i in subps: - steps = UsedStep.objects.filter(subproduction=i, is_deleted=False, subproduction__is_deleted=False, step__is_deleted=False)\ - .order_by('step__number').annotate(id=F('step__id'), number=F('step__number'), name=F('step__name')).values('id', 'number', 'name', 'remark') + steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False, + usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark') SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, start_date=production_plan.start_date, end_date=production_plan.end_date, workshop=i.process.workshop, process=i.process, create_by=request.user, @@ -96,6 +96,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo return SubProductionPlanListSerializer elif self.action == 'update': return SubProductionPlanUpdateSerializer + return SubProductionPlanListSerializer class ResourceViewSet(GenericViewSet): @@ -139,7 +140,7 @@ class ResourceViewSet(GenericViewSet): for i in rdata: rdata_l.append(i['id']) subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False) - steps = Step.objects.filter(usedsteps__is_deleted=False, usedsteps__subproduction__in=subproductions) + steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions) equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False) serializer = EquipmentSerializer(instance=equips, many=True) return Response(serializer.data) From ce43a37c040eb7fd9ef0de8334c23a08e18887bb Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 15 Oct 2021 13:47:22 +0800 Subject: [PATCH 03/31] =?UTF-8?q?=E5=8A=A0=E7=AD=BE=E5=AE=8C=E6=88=90bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index a96006c..438c60d 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -342,8 +342,8 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin ticket = self.get_object() ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL ticket.in_add_node = False - ticket.add_node_man = None ticket.participant = ticket.add_node_man.id + ticket.add_node_man = None ticket.save() # 更新流转记录 suggestion = request.data.get('suggestion', '') # 加签意见 From 2a3b7424b3d42826ceaa35fb20e567efb1577300 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 15 Oct 2021 13:57:30 +0800 Subject: [PATCH 04/31] =?UTF-8?q?=E6=88=91=E5=A4=84=E7=90=86=E7=9A=84bug?= =?UTF-8?q?=20=E9=9C=80=E8=A6=81=E5=8E=BB=E9=87=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wf/filters.py b/hb_server/apps/wf/filters.py index 7e2990c..742af75 100644 --- a/hb_server/apps/wf/filters.py +++ b/hb_server/apps/wf/filters.py @@ -16,7 +16,7 @@ class TicketFilterSet(filters.FilterSet): elif value == 'duty': queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) elif value == 'worked': - queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user) + queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user).order_by('-update_time').distinct() elif value == 'all': pass else: From 5ac4ef99850459739b283176ec528c34c4ba05ac Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 15 Oct 2021 14:09:17 +0800 Subject: [PATCH 05/31] =?UTF-8?q?=E5=B7=A5=E5=8D=95=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E7=89=A9=E7=90=86=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 5 ++++- hb_server/apps/wf/views.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 2029ec8..45369a5 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -139,4 +139,7 @@ class TicketAddNodeSerializer(serializers.Serializer): toadd_user = serializers.IntegerField(label='发送给谁去加签') class TicketAddNodeEndSerializer(serializers.Serializer): - suggestion = serializers.CharField(label="加签意见", required = False) \ No newline at end of file + suggestion = serializers.CharField(label="加签意见", required = False) + +class TicketDestorySerializer(serializers.Serializer): + ids = serializers.ListField(child=serializers.IntegerField(), label='工单ID列表') \ No newline at end of file diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 438c60d..eab077e 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -4,7 +4,7 @@ from django.core.exceptions import AppRegistryNotReady from rest_framework.response import Response from rest_framework import serializers from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin -from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer +from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from django.shortcuts import get_object_or_404, render from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.decorators import action, api_view @@ -375,6 +375,15 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin else: return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST) + @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=TicketDestorySerializer) + def destory(self, request, pk=None): + """ + 批量物理删除 + """ + Ticket.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False) + return Response() + + class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ From 22b52686bc61b956d3f55640bc4ffb4833ad377e Mon Sep 17 00:00:00 2001 From: shijing Date: Fri, 15 Oct 2021 16:03:50 +0800 Subject: [PATCH 06/31] ticketdelete --- hb_client/src/api/workflow.js | 34 +- hb_client/src/router/index.js | 43 +- hb_client/src/styles/index.scss | 28 +- hb_client/src/views/login/index.vue | 5 +- hb_client/src/views/workflow/index.vue | 52 +- hb_client/src/views/workflow/state.vue | 554 +++++++++--------- hb_client/src/views/workflow/ticket.vue | 228 +++++-- hb_client/src/views/workflow/ticketHandle.vue | 206 ++++++- .../src/views/workflow/workFlowTickets.vue | 197 +++++++ 9 files changed, 933 insertions(+), 414 deletions(-) create mode 100644 hb_client/src/views/workflow/workFlowTickets.vue diff --git a/hb_client/src/api/workflow.js b/hb_client/src/api/workflow.js index 137b132..069f700 100644 --- a/hb_client/src/api/workflow.js +++ b/hb_client/src/api/workflow.js @@ -163,13 +163,45 @@ export function ticketAccpet(id,data) { }) } //撤回工单,允许创建人在指定状态撤回工单至初始状态 -export function getTicketRetreat(id,data) { +export function ticketRetreat(id,data) { return request({ url: `/wf/ticket/${id}/retreat/`, method: 'post', data }) } +//关闭工单,仅允许创建人在初始状态关闭工单 +export function ticketAddNode(id,data) { + return request({ + url: `/wf/ticket/${id}/add_node/`, + method: 'post', + data + }) +} +//加签 +export function ticketClose(id,data) { + return request({ + url: `/wf/ticket/${id}/close/`, + method: 'post', + data + }) +} +//加签 +export function ticketAddNodeEnd(id,data) { + return request({ + url: `/wf/ticket/${id}/add_node_end/`, + method: 'post', + data + }) +} +//工单删除 +export function ticketDestory(data) { + return request({ + url: `/wf/ticket/destory/`, + method: 'post', + data + }) +} //工单详情 export function getTicketDetail(id) { return request({ diff --git a/hb_client/src/router/index.js b/hb_client/src/router/index.js index 4b8ad23..c021e2c 100644 --- a/hb_client/src/router/index.js +++ b/hb_client/src/router/index.js @@ -86,13 +86,13 @@ export const asyncRoutes = [ component: Layout, redirect: '/mtm/material/', name: 'mtm', - meta: { title: '制造管理', icon: 'example', perms: ['procurement_set'] }, + meta: { title: '制造管理', icon: 'example', perms: ['mtm_manage'] }, children: [ { path: 'material', name: 'material', component: () => import('@/views/mtm/material'), - meta: { title: '物料清单', icon: 'example', perms: ['vendor_manage'] } + meta: { title: '物料清单', icon: 'example', perms: ['mtm_material'] } } , { @@ -106,7 +106,7 @@ export const asyncRoutes = [ path: 'process', name: 'process', component: () => import('@/views/mtm/process'), - meta: { title: '工序管理', icon: 'example', perms: ['vendor_manage'] } + meta: { title: '工序管理', icon: 'example', perms: ['mtm_process'] } }, { path: 'step/:id', @@ -127,7 +127,7 @@ export const asyncRoutes = [ path: '/mtm/productprocess/', name: 'productprocess', component: () => import('@/views/mtm/productprocess'), - meta: { title: '产品管理', icon: 'example', perms: ['vendor_manage'] } + meta: { title: '产品管理', icon: 'example', perms: ['mtm_productprocess'] } }, ] } @@ -137,25 +137,25 @@ export const asyncRoutes = [ component: Layout, redirect: '/pm/plan', name: 'pm', - meta: { title: '生产管理', icon: 'example', perms: ['equipment_set'] }, + meta: { title: '生产管理', icon: 'example', perms: ['pm_manage'] }, children: [ { path: 'plan', name: 'plan', component: () => import('@/views/pm/plan'), - meta: { title: '生产计划管理', icon: 'example', perms: ['index_manage'] } + meta: { title: '生产计划管理', icon: 'example', perms: ['pm_plan'] } }, { path: 'resources', name: 'resources', component: () => import('@/views/pm/resources'), - meta: { title: '生产资源配置', icon: 'example', perms: ['index_manage'] } + meta: { title: '生产资源配置', icon: 'example', perms: ['pm_resources'] } }, { path: 'testitem', name: 'testitem', component: () => import('@/views/pm/plan'), - meta: { title: '生产作业管理', icon: 'example', perms: ['index_manage'] } + meta: { title: '生产作业管理', icon: 'example', perms: ['pm_testitem'] } } ] }, @@ -164,31 +164,31 @@ export const asyncRoutes = [ component: Layout, redirect: '/em/equipment', name: 'em', - meta: { title: '设备管理', icon: 'example', perms: ['equipment_set'] }, + meta: { title: '设备管理', icon: 'example', perms: ['em_manage'] }, children: [ { path: 'equipment', name: 'equipment', component: () => import('@/views/em/equipment'), - meta: { title: '生产设备', icon: 'example', perms: ['index_manage'] } + meta: { title: '生产设备', icon: 'example', perms: ['em_equipment'] } }, { path: 'detection ', name: 'detection ', component: () => import('@/views/em/detection'), - meta: { title: '监视和测量设备', icon: 'example', perms: ['index_manage'] } + meta: { title: '监视和测量设备', icon: 'example', perms: ['em_detection'] } }, { path: 'record', name: 'record', component: () => import('@/views/em/record'), - meta: { title: '校准检定记录', icon: 'example', perms: ['index_manage'] } + meta: { title: '校准检定记录', icon: 'example', perms: ['em_record'] } }, { path: 'detection ', name: 'detection ', component: () => import('@/views/em/detection'), - meta: { title: '运维记录', icon: 'example', perms: ['index_manage'] } + meta: { title: '运维记录', icon: 'example', perms: ['em_detection'] } } ] }, @@ -319,32 +319,39 @@ export const asyncRoutes = [ component: Layout, redirect: '/workflow/index', name: 'workflow', - meta: { title: '工作流', icon: 'example', perms: ['workflow_set'] }, + meta: { title: '工作流', icon: 'example', perms: ['workflow_manage'] }, children: [ { path: 'index', name: 'index', component: () => import('@/views/workflow/index'), - meta: { title: '工作流配置', icon: 'example', perms: ['workflow_manage'] } + meta: { title: '工作流配置', icon: 'example', perms: ['workflow_index'] } }, { path: 'ticket', name: 'ticket', component: () => import('@/views/workflow/ticket'), - meta: { title: '工单管理', icon: 'example', perms: ['workflow_manage'] }, + meta: { title: '工单管理', icon: 'example' ,noCache: true, perms: ['workflow_ticket'] }, + }, + { + path: 'workFlowTickets', + name: 'workFlowTickets', + component: () => import('@/views/workflow/workFlowTickets'), + meta: { title: '工单管理', icon: 'example' ,noCache: true,}, + hidden: true }, { path: 'configuration', name: 'configuration', component: () => import('@/views/workflow/configuration'), - meta: { title: '人员信息详情', icon: 'example', perms: ['workflow_manage'] }, + meta: { title: '人员信息详情', icon: 'example' }, hidden: true }, { path: 'ticketHandle', name: 'ticketHandle', component: () => import('@/views/workflow/ticketHandle'), - meta: { title: '工单处理', icon: 'example', perms: ['workflow_manage'] }, + meta: { title: '工单处理', icon: 'example',noCache: true,}, hidden: true }, ] diff --git a/hb_client/src/styles/index.scss b/hb_client/src/styles/index.scss index 059de63..d80b15e 100644 --- a/hb_client/src/styles/index.scss +++ b/hb_client/src/styles/index.scss @@ -86,21 +86,21 @@ div:focus { .el-dialog__header { padding: 10px 10px 6px; } -// .el-dialog{ -// display: flex; -// flex-direction: column; -// margin:0 !important; -// position:absolute; -// top:50%; -// left:50%; -// transform:translate(-50%,-50%); -// /*height:600px;*/ -// max-height:calc(100% - 30px); -// max-width:calc(100% - 30px); -// } + .el-dialog{ + display: flex; + flex-direction: column; + margin:0 !important; + position:absolute; + top:50%; + left:50%; + transform:translate(-50%,-50%); + /*height:600px;*/ + max-height:calc(100% - 30px); + max-width:calc(100% - 30px); + } .el-dialog .el-dialog__body{ - // flex:1; - // overflow: auto; + flex:1; + overflow: auto; padding: 8px 12px; } diff --git a/hb_client/src/views/login/index.vue b/hb_client/src/views/login/index.vue index f9ad27a..07fd8f8 100644 --- a/hb_client/src/views/login/index.vue +++ b/hb_client/src/views/login/index.vue @@ -37,6 +37,7 @@ name="password" tabindex="2" auto-complete="on" + id="passwordInput" @keyup.enter.native="handleLogin" >创建时间 :{{watchedCreateTime}}

- + @@ -278,7 +278,7 @@ export default { }); }, handleTicket(scope){ - this.$router.push({name:"ticket",params:{workflow:scope.row.id}}) + this.$router.push({name:"workFlowTickets",params:{workflow:scope.row.id}}) }, async confirm(form) { debugger; @@ -321,12 +321,12 @@ export default { that.limitedWatch = true; that.$nextTick(()=>{ var g = new dagreD3.graphlib.Graph().setGraph({ - align: 'DL', + rankdir: 'DL', nodesep: 100, - edgesep: 100, - ranksep: 50, - marginx: 0, - marginy: 50, + edgesep: 10,//两条线之间的距离 + ranksep: 50,//节点之间的距离 + marginx: 160, + marginy: 20, }); //获取state得到节点 getWfStateList(workFlow).then((response) => { @@ -343,8 +343,6 @@ export default { //节点样式 style: "fill:#fff;stroke:#000", labelStyle: "fill:#000;", - // width: 83, - // height: 40, rx :5,//矩形节点圆角度 ry :5 }); @@ -356,9 +354,8 @@ export default { getWfTransitionList(workFlow).then((res)=>{ if(res.data){ let transitionList = res.data; - transitionList.forEach((transitions)=>{ - let transition0 = transitions; - if (transition0.condition_expression.length>3){ + transitionList.forEach((transition0)=>{ + if (transition0.condition_expression.length>0){ debugger; g.setNode(transition0.source_state_.id+100000, {label: "条件表达式",style: "stroke: #000;fill: #afa", shape: "diamond"}); g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, { @@ -366,7 +363,7 @@ export default { label: transition0.name, style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" }); - let condition_expression = JSON.parse(transition0.condition_expression); + let condition_expression = transition0.condition_expression; condition_expression.forEach(condition_expression0=>{ g.setEdge(transition0.source_state_.id+100000, condition_expression0.target_state, { label: condition_expression0.label, @@ -386,7 +383,6 @@ export default { g.nodes().forEach(function (v) { console.log("Node " + v + ": " + JSON.stringify(g.node(v))); }); - // 创建渲染器 let render = new dagreD3.render(); // 选择 svg 并添加一个g元素作为绘图容器. @@ -399,6 +395,7 @@ export default { } }); + }) }, closeMark(){ @@ -415,33 +412,25 @@ export default { this.hasJsonFlag = true }, onError(value) { - // console.log("json错误了value:", value); this.hasJsonFlag = false }, onJsonChange1 (value) { - // console.log('更改value:', value); // 实时保存 this.onJsonSave1(value) }, onJsonSave1 (value) { - // console.log('保存value:', value); this.display_form_str = value this.hasJsonFlag1 = true }, onError1(value) { - // console.log("json错误了value:", value); this.hasJsonFlag1 = false }, // 检查json checkJson(){ if (this.hasJsonFlag == false){ - // console.log("json验证失败") - // this.$message.error("json验证失败") alert("限制表达式json验证失败") return false } else { - // console.log("json验证成功") - // this.$message.success("json验证成功") alert("限制表达式json验证成功") return true } @@ -449,13 +438,9 @@ export default { // 检查json checkJson2(){ if (this.hasJsonFlag1 == false){ - // console.log("json验证失败") - // this.$message.error("json验证失败") alert("展现表单字段json验证失败") return false } else { - // console.log("json验证成功") - // this.$message.success("json验证成功") alert("展现表单字段json1验证成功") return true } @@ -464,6 +449,19 @@ export default { };
- -
- 新增 -
+
+ 新增 + +
- - + + - - diff --git a/hb_client/src/views/workflow/ticket.vue b/hb_client/src/views/workflow/ticket.vue index f3e13ea..c482c7f 100644 --- a/hb_client/src/views/workflow/ticket.vue +++ b/hb_client/src/views/workflow/ticket.vue @@ -61,10 +61,13 @@ @@ -107,7 +110,12 @@ @@ -150,10 +158,11 @@ @@ -197,7 +206,7 @@ @@ -224,25 +233,27 @@ - - +
+ + +
+
- + - - - - - + + + +
- 取消 - 确定 + 取消 + 确定
@@ -337,13 +348,49 @@ 确认 + + + + + + + + + + + + + + + + + + From 7dc91b5a86051424213aa0370abb8ee7ccafe052 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 18 Oct 2021 11:00:38 +0800 Subject: [PATCH 07/31] =?UTF-8?q?=E5=88=A4=E5=AE=9A=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mtm/migrations/0023_auto_20211018_1057.py | 49 +++++++++++++++++++ hb_server/apps/mtm/models.py | 15 ++++++ hb_server/apps/mtm/serializers.py | 4 +- hb_server/apps/wf/models.py | 18 ++++++- hb_server/apps/wf/services.py | 4 +- hb_server/apps/wf/views.py | 6 +-- 6 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0023_auto_20211018_1057.py diff --git a/hb_server/apps/mtm/migrations/0023_auto_20211018_1057.py b/hb_server/apps/mtm/migrations/0023_auto_20211018_1057.py new file mode 100644 index 0000000..9cf0179 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0023_auto_20211018_1057.py @@ -0,0 +1,49 @@ +# Generated by Django 3.2.6 on 2021-10-18 02:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0022_auto_20211014_0944'), + ] + + operations = [ + migrations.AddField( + model_name='recordformfield', + name='high_limit', + field=models.FloatField(blank=True, null=True, verbose_name='上限值'), + ), + migrations.AddField( + model_name='recordformfield', + name='high_rule', + field=models.IntegerField(blank=True, choices=[(1, '小于'), (2, '小于等于')], null=True, verbose_name='上限规则'), + ), + migrations.AddField( + model_name='recordformfield', + name='low_limit', + field=models.FloatField(blank=True, null=True, verbose_name='下限值'), + ), + migrations.AddField( + model_name='recordformfield', + name='low_rule', + field=models.IntegerField(blank=True, choices=[(1, '大于'), (2, '大于等于')], null=True, verbose_name='下限规则'), + ), + migrations.AddField( + model_name='recordformfield', + name='need_judge', + field=models.BooleanField(default=False, verbose_name='需要判定'), + ), + migrations.AddField( + model_name='recordformfield', + name='rule_expression', + field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充工单的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'), + ), + migrations.AlterField( + model_name='usedstep', + name='step', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usedstep', to='mtm.step', verbose_name='子工序'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 7675957..6ebe98a 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -110,6 +110,14 @@ class RecordFormField(CommonAModel): ('selects', '多选下拉'), ('textarea', '文本域'), ) + high_rule_choices = ( + (1, '小于'), + (2, '小于等于'), + ) + low_rule_choices = ( + (1, '大于'), + (2, '大于等于'), + ) form = models.ForeignKey(RecordForm, on_delete=models.CASCADE, verbose_name='关联表格') field_type = models.CharField('类型', max_length=50, choices=field_type_choices) field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突') @@ -119,6 +127,13 @@ class RecordFormField(CommonAModel): field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True, null=True, help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号') sort = models.IntegerField('排序号', default=1) + need_judge = models.BooleanField('需要判定', default=False) + high_limit = models.FloatField('上限值', null=True, blank=True) + high_rule = models.IntegerField('上限规则', choices=high_rule_choices, null=True, blank=True) + low_limit = models.FloatField('下限值', null=True, blank=True) + low_rule = models.IntegerField('下限规则', choices=low_rule_choices, null=True, blank=True) + rule_expression = models.JSONField('判定表达式', default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突' ) + class Meta: verbose_name = '记录表格字段' verbose_name_plural = verbose_name diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 5da68a4..315e464 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -175,7 +175,7 @@ class RecordFormFieldSerializer(serializers.ModelSerializer): class RecordFormFieldCreateSerializer(serializers.ModelSerializer): class Meta: model = RecordFormField - fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort'] + fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression'] def validate(self, data): if RecordFormField.objects.filter(field_key=data['field_key'], form=data['form'], is_deleted=False).exists(): @@ -185,7 +185,7 @@ class RecordFormFieldCreateSerializer(serializers.ModelSerializer): class RecordFormFieldUpdateSerializer(serializers.ModelSerializer): class Meta: model = RecordFormField - fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort'] + fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression'] class RecordFormFieldSimpleSerializer(serializers.ModelSerializer): class Meta: diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 4540eb0..5427ed2 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -207,4 +207,20 @@ class TicketFlow(BaseModel): participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant') state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE) ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式') - intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices) \ No newline at end of file + intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices) + + +class WfScript(CommonAModel): + """ + 执行脚本 + """ + usage_choices =( + (1, '获取处理人'), + (2, '执行操作'), + ) + usage = models.IntegerField('脚本用途', default=1, choices=usage_choices) + wait = models.BooleanField('是否等待执行完成', default=True) + name = models.CharField('脚本名称', max_length=100) + workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL) + content = models.TextField('脚本内容') + diff --git a/hb_server/apps/wf/services.py b/hb_server/apps/wf/services.py index a6ec576..0ddfec3 100644 --- a/hb_server/apps/wf/services.py +++ b/hb_server/apps/wf/services.py @@ -141,8 +141,8 @@ class WfService(object): multi_all_person_dict = {} destination_participant_type, destination_participant = state.participant_type, state.participant if destination_participant_type == State.PARTICIPANT_TYPE_FIELD: - destination_participant = ticket_data.get(destination_participant, None) if destination_participant in ticket_data else Ticket.ticket_data.get(destination_participant, None) - + destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \ + else Ticket.ticket_data.get(destination_participant, 0) elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#单部门 destination_participant = list(User.objects.filter(dept=destination_participant).values_list('id', flat=True)) diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index eab077e..3d3f439 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -136,7 +136,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition) participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data) destination_participant_type = participant_info.get('destination_participant_type', 0) - destination_participant = participant_info.get('destination_participant', None) + destination_participant = participant_info.get('destination_participant', 0) multi_all_person = participant_info.get('multi_all_person', {}) # 多人需要全部处理情况 sn = WfService.get_ticket_sn(ticket.workflow) # 流水号 if next_state.type == State.STATE_TYPE_END: @@ -197,7 +197,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin if WfService.check_dict_has_all_same_value(multi_all_person): participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data']) destination_participant_type = participant_info.get('destination_participant_type', 0) - destination_participant = participant_info.get('destination_participant', None) + destination_participant = participant_info.get('destination_participant', 0) multi_all_person = {} else: # 处理人没有没有全部处理完成或者处理动作不一致 @@ -211,7 +211,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin # 当前处理人类型非全部处理 participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data']) destination_participant_type = participant_info.get('destination_participant_type', 0) - destination_participant = participant_info.get('destination_participant', None) + destination_participant = participant_info.get('destination_participant', 0) multi_all_person = participant_info.get('multi_all_person', {}) # 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况 From 7509ff0e1582feff089f940bf7a7bb8f0c3d06fa Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 18 Oct 2021 14:18:11 +0800 Subject: [PATCH 08/31] =?UTF-8?q?=E4=BA=BA=E8=84=B8=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_client/.env.production | 3 +- .../hrm/migrations/0003_employee_face_data.py | 18 ++++ hb_server/apps/hrm/models.py | 5 +- hb_server/apps/hrm/serializers.py | 5 +- hb_server/apps/hrm/urls.py | 3 +- hb_server/apps/hrm/views.py | 96 ++++++++++++++++++- hb_server/apps/system/serializers.py | 2 - hb_server/apps/system/urls.py | 5 +- hb_server/apps/system/views.py | 43 +-------- 9 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 hb_server/apps/hrm/migrations/0003_employee_face_data.py diff --git a/hb_client/.env.production b/hb_client/.env.production index 484e806..b79ed31 100644 --- a/hb_client/.env.production +++ b/hb_client/.env.production @@ -2,5 +2,6 @@ ENV = 'production' # base api -VUE_APP_BASE_API = 'http://47.95.0.242:2222/api' +#VUE_APP_BASE_API = 'http://47.95.0.242:2222/api' +VUE_APP_BASE_API = 'http://127.0.0.1:8000/api' diff --git a/hb_server/apps/hrm/migrations/0003_employee_face_data.py b/hb_server/apps/hrm/migrations/0003_employee_face_data.py new file mode 100644 index 0000000..1823ad2 --- /dev/null +++ b/hb_server/apps/hrm/migrations/0003_employee_face_data.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-10-18 05:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hrm', '0002_auto_20210924_1127'), + ] + + operations = [ + migrations.AddField( + model_name='employee', + name='face_data', + field=models.JSONField(blank=True, null=True, verbose_name='人脸识别数据'), + ), + ] diff --git a/hb_server/apps/hrm/models.py b/hb_server/apps/hrm/models.py index c448e64..1176416 100644 --- a/hb_server/apps/hrm/models.py +++ b/hb_server/apps/hrm/models.py @@ -28,9 +28,12 @@ class Employee(CommonAModel): academic = models.CharField('学历', max_length=50, null=True, blank=True) jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1) job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位') + face_data = models.JSONField('人脸识别数据', null=True, blank=True) class Meta: verbose_name = '员工补充信息' verbose_name_plural = verbose_name def __str__(self): - return self.name \ No newline at end of file + return self.name + + diff --git a/hb_server/apps/hrm/serializers.py b/hb_server/apps/hrm/serializers.py index 96ab4df..7a14400 100644 --- a/hb_server/apps/hrm/serializers.py +++ b/hb_server/apps/hrm/serializers.py @@ -1,6 +1,6 @@ from apps.system.models import User from rest_framework.serializers import ModelSerializer - +from rest_framework import serializers from .models import Employee from apps.system.serializers import UserListSerializer, UserSimpleSerializer from django.db.models.query import Prefetch @@ -20,3 +20,6 @@ class EmployeeSerializer(ModelSerializer): # queryset=User.objects.filter(employee_user__isnull=True)) # ) # return queryset + +class FaceLoginSerializer(serializers.Serializer): + base64 = serializers.CharField() diff --git a/hb_server/apps/hrm/urls.py b/hb_server/apps/hrm/urls.py index bd532d0..293124c 100644 --- a/hb_server/apps/hrm/urls.py +++ b/hb_server/apps/hrm/urls.py @@ -1,12 +1,13 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.hrm.views import EmployeeViewSet +from apps.hrm.views import EmployeeViewSet, FaceLogin from django.urls import path, include from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('employee', EmployeeViewSet, basename='employee') urlpatterns = [ + path('facelogin/', FaceLogin.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 843fa96..ffb8df3 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -1,9 +1,34 @@ from django.shortcuts import render +from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.hrm.models import Employee -from apps.hrm.serializers import EmployeeSerializer +from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer +import face_recognition +from django.conf import settings +from django.core.cache import cache +import logging +from rest_framework.generics import CreateAPIView +from rest_framework import status +from rest_framework_simplejwt.tokens import RefreshToken + +from apps.system.models import User +logger = logging.getLogger('log') + + +def load_face_data(username:int, path:str): + """ + 将某用户face_encoding加载进缓存 + """ + face_datas = cache.get_or_set('face_datas', {}, timeout=None) + photo_path = settings.BASE_DIR + path + picture_of_me = face_recognition.load_image_file(photo_path) + my_face_encoding = face_recognition.face_encodings(picture_of_me)[0] + face_datas[username] = my_face_encoding + cache.set('face_datas', face_datas, timeout=None) + return my_face_encoding + # Create your views here. class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet): """ @@ -12,4 +37,71 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix perms_map = {'get': '*', 'put': 'employee_update'} queryset = Employee.objects.all() serializer_class = EmployeeSerializer - ordering = ['-pk'] \ No newline at end of file + ordering = ['-pk'] + + def perform_update(self, serializer): + instance = serializer.save(update_by = self.request.user) + try: + photo_path = settings.BASE_DIR + instance.photo + picture_of_me = face_recognition.load_image_file(photo_path) + my_face_encoding = face_recognition.face_encodings(picture_of_me)[0] + instance.face_data = my_face_encoding.tolist() + instance.save() + except: + logger.error('人脸识别出错') + +import uuid +import base64 +import os + +def tran64(s): + missing_padding = len(s) % 4 + if missing_padding != 0: + s = s+'='* (4 - missing_padding) + return s + +class FaceLogin(CreateAPIView): + authentication_classes = [] + permission_classes = [] + serializer_class = FaceLoginSerializer + + + def create(self, request, *args, **kwargs): + """ + 人脸识别登录 + """ + # serializer = FaceLoginSerializer(data=request.data) + # serializer.is_valid(raise_exception=True) + filename = str(uuid.uuid4()) + filepath = settings.BASE_DIR +'/temp/' + filename +'.png' + with open(filepath, 'wb') as f: + data = tran64(request.data.get('base64').replace(' ', '+')) + f.write(base64.urlsafe_b64decode(data)) + # picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png') + # my_face_encoding = face_recognition.face_encodings(picture_of_me)[0] + #results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2) + try: + unknown_picture = face_recognition.load_image_file(filepath) + unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0] + os.remove(filepath) + except: + logger.error('解码失败') + + # 匹配人脸库 + user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data') + user_l = [] + face_l = [] + for i in user_faces: + user_l.append(i['user']) + face_l.append(i['face_data']) + + results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.2) + for index, value in enumerate(results): + if value: + # 识别成功 + refresh = RefreshToken.for_user(User.objects.get(id=user_l[index])) + return Response({ + 'refresh': str(refresh), + 'access': str(refresh.access_token), + }) + return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index a39e88b..10636ea 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -199,5 +199,3 @@ class UserCreateSerializer(serializers.ModelSerializer): return phone -class FaceLoginSerializer(serializers.Serializer): - base64 = serializers.CharField() \ No newline at end of file diff --git a/hb_server/apps/system/urls.py b/hb_server/apps/system/urls.py index 55e1420..02a4ecb 100644 --- a/hb_server/apps/system/urls.py +++ b/hb_server/apps/system/urls.py @@ -1,5 +1,5 @@ from django.urls import path, include -from .views import FaceLogin, TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet +from .views import TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet from rest_framework import routers @@ -15,6 +15,5 @@ router.register('ptask', PTaskViewSet, basename="ptask") urlpatterns = [ path('', include(router.urls)), path('task/', TaskList.as_view()), - path('test/', TestView.as_view()), - path('facelogin/', FaceLogin.as_view()) + path('test/', TestView.as_view()) ] diff --git a/hb_server/apps/system/views.py b/hb_server/apps/system/views.py index 2ee96a6..65a1320 100644 --- a/hb_server/apps/system/views.py +++ b/hb_server/apps/system/views.py @@ -21,6 +21,7 @@ from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework_simplejwt.tokens import RefreshToken from rest_framework.exceptions import ValidationError, ParseError +from apps.hrm.models import Employee from utils.queryset import get_child_queryset2 from .filters import UserFilter @@ -29,7 +30,7 @@ from .models import (Dict, DictType, File, Organization, Permission, Position, Role, User) from .permission import RbacPermission, get_permission_list from .permission_data import RbacFilterSet -from .serializers import (DictSerializer, DictTypeSerializer, FaceLoginSerializer, FileSerializer, +from .serializers import (DictSerializer, DictTypeSerializer, FileSerializer, OrganizationSerializer, PermissionSerializer, PositionSerializer, RoleSerializer, PTaskSerializer,PTaskCreateUpdateSerializer, UserCreateSerializer, UserListSerializer, @@ -352,43 +353,3 @@ class FileViewSet(CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListM instance.save() - -#import face_recognition -import uuid -import base64 -import os - -def tran64(s): - missing_padding = len(s) % 4 - if missing_padding != 0: - s = s+'='* (4 - missing_padding) - return s - -class FaceLogin(CreateAPIView): - authentication_classes = [] - permission_classes = [] - serializer_class = FaceLoginSerializer - - - def create(self, request, *args, **kwargs): - """ - 人脸识别登录 - """ - # serializer = FaceLoginSerializer(data=request.data) - # serializer.is_valid(raise_exception=True) - filename = str(uuid.uuid4()) - filepath = settings.BASE_DIR +'/temp/' + filename +'.png' - with open(filepath, 'wb') as f: - data = tran64(request.data.get('base64').replace(' ', '+')) - f.write(base64.urlsafe_b64decode(data)) - # picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png') - # my_face_encoding = face_recognition.face_encodings(picture_of_me)[0] - # unknown_picture = face_recognition.load_image_file(filepath) - # unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0] - #results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2) - os.remove(filepath) - # if results[0] == True: - # return Response('这是曹前明') - # else: - # return Response('这不是曹前明') - From 7b6f5008ce43fc01675db1b2fa2f4da83c53abb1 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 18 Oct 2021 14:46:05 +0800 Subject: [PATCH 09/31] =?UTF-8?q?=E4=BA=BA=E8=84=B8=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E7=B2=BE=E5=BA=A6=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/hrm/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index ffb8df3..595285d 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -95,13 +95,15 @@ class FaceLogin(CreateAPIView): user_l.append(i['user']) face_l.append(i['face_data']) - results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.2) + results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5) for index, value in enumerate(results): if value: # 识别成功 - refresh = RefreshToken.for_user(User.objects.get(id=user_l[index])) + user = User.objects.get(id=user_l[index]) + refresh = RefreshToken.for_user(user) return Response({ 'refresh': str(refresh), 'access': str(refresh.access_token), + 'username':user.username }) return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file From 1d056a775f88284e71b0b796eae13496b9f9adcc Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 Oct 2021 09:46:44 +0800 Subject: [PATCH 10/31] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AD=90=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E8=BF=9B=E5=BA=A6=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_client/.env.production | 4 +- hb_server/apps/hrm/views.py | 2 +- ...4_alter_recordformfield_rule_expression.py | 18 +++++++++ .../pm/migrations/0005_auto_20211019_0944.py | 38 +++++++++++++++++++ hb_server/apps/pm/models.py | 27 ++++++++++--- hb_server/apps/pm/serializers.py | 7 +++- hb_server/apps/pm/views.py | 17 +++++++-- 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0024_alter_recordformfield_rule_expression.py create mode 100644 hb_server/apps/pm/migrations/0005_auto_20211019_0944.py diff --git a/hb_client/.env.production b/hb_client/.env.production index b79ed31..d214255 100644 --- a/hb_client/.env.production +++ b/hb_client/.env.production @@ -2,6 +2,6 @@ ENV = 'production' # base api -#VUE_APP_BASE_API = 'http://47.95.0.242:2222/api' -VUE_APP_BASE_API = 'http://127.0.0.1:8000/api' +VUE_APP_BASE_API = 'http://47.95.0.242:2222/api' +#VUE_APP_BASE_API = 'http://127.0.0.1:8000/api' diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 595285d..151fa06 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -85,7 +85,7 @@ class FaceLogin(CreateAPIView): unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0] os.remove(filepath) except: - logger.error('解码失败') + return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST) # 匹配人脸库 user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data') diff --git a/hb_server/apps/mtm/migrations/0024_alter_recordformfield_rule_expression.py b/hb_server/apps/mtm/migrations/0024_alter_recordformfield_rule_expression.py new file mode 100644 index 0000000..f31fbc4 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0024_alter_recordformfield_rule_expression.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-10-19 01:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0023_auto_20211018_1057'), + ] + + operations = [ + migrations.AlterField( + model_name='recordformfield', + name='rule_expression', + field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'), + ), + ] diff --git a/hb_server/apps/pm/migrations/0005_auto_20211019_0944.py b/hb_server/apps/pm/migrations/0005_auto_20211019_0944.py new file mode 100644 index 0000000..46824e4 --- /dev/null +++ b/hb_server/apps/pm/migrations/0005_auto_20211019_0944.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.6 on 2021-10-19 01:44 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0024_alter_recordformfield_rule_expression'), + ('pm', '0004_subproductionplan_steps'), + ] + + operations = [ + migrations.AddField( + model_name='subproductionplan', + name='state', + field=models.IntegerField(default=0, verbose_name='状态'), + ), + migrations.CreateModel( + name='SubProductionProgress', + fields=[ + ('id', models.BigAutoField(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='删除标记')), + ('type', models.IntegerField(default=1, verbose_name='物料应用类型')), + ('count', models.IntegerField(verbose_name='应出入数')), + ('count_real', models.IntegerField(verbose_name='实际出入数')), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')), + ('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_subplan', to='pm.subproductionplan', verbose_name='关联子生产计划')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 5aab5f3..418e4dc 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -31,6 +31,13 @@ class SubProductionPlan(CommonAModel): """ 子生产计划 """ + state_choices=( + (0, '制定中'), + (1, '已下达'), + (2, '已接收'), + (3, '生产中'), + (4, '已完成') + ) production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) start_date = models.DateField('计划开工日期') @@ -38,13 +45,21 @@ class SubProductionPlan(CommonAModel): workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) steps = models.JSONField('工艺步骤', default=list) + state = models.IntegerField('状态', default=0) class Meta: verbose_name = '子生产计划' verbose_name_plural = verbose_name -# class ProductionProgress(BaseModel): -# """ -# 子计划生产进度 -# """ -# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE) -# material = models. \ No newline at end of file +class SubProductionProgress(BaseModel): + """ + 子计划生产进度统计表 + """ + type_choices=( + (1, '输入物料'), + (2, '输出物料') + ) + subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan') + material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) + type = models.IntegerField('物料应用类型', default=1) + count = models.IntegerField('应出入数') + count_real = models.IntegerField('实际出入数') diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 1950bce..7c79bc0 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -1,4 +1,4 @@ -from apps.pm.models import ProductionPlan, SubProductionPlan +from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from rest_framework import serializers from apps.sam.serializers import OrderSerializer from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer @@ -38,3 +38,8 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): class GenSubPlanSerializer(serializers.Serializer): pass + +class SubProductionProgressSerializer(serializers.ModelSerializer): + material_ = MaterialSimpleSerializer(source='material', read_only=True) + class Meta: + model = SubProductionProgress \ No newline at end of file diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index a9856cd..4805099 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -2,11 +2,11 @@ from rest_framework import serializers from rest_framework.views import APIView from apps.em.models import Equipment from apps.em.serializers import EquipmentSerializer -from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep +from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep from apps.system.mixins import CreateUpdateModelAMixin -from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer +from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin -from apps.pm.models import ProductionPlan, SubProductionPlan +from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from rest_framework.viewsets import GenericViewSet, ModelViewSet from django.shortcuts import render from apps.sam.models import Order @@ -72,10 +72,14 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel for i in subps: steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False, usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark') - SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, + instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, start_date=production_plan.start_date, end_date=production_plan.end_date, workshop=i.process.workshop, process=i.process, create_by=request.user, steps = list(steps)) + for m in InputMaterial.objects.filter(subproduction=i, is_delete=False).order_by('sort'): + SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance) + for m in OutputMaterial.objects.filter(subproduction=i, is_delete=False).order_by('sort'): + SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance) production_plan.is_planed=True production_plan.save() return Response() @@ -98,6 +102,11 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo return SubProductionPlanUpdateSerializer return SubProductionPlanListSerializer + @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer) + def progress(self, request, pk=None): + obj = self.get_object() + serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True) + return Response(serializer.data) class ResourceViewSet(GenericViewSet): From da1bb4e320552b3ecc047a12e0a769830d170213 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 Oct 2021 09:54:45 +0800 Subject: [PATCH 11/31] =?UTF-8?q?=E5=AD=90=E8=AE=A1=E5=88=92=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/pm/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 4805099..d50ffae 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -76,9 +76,9 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel start_date=production_plan.start_date, end_date=production_plan.end_date, workshop=i.process.workshop, process=i.process, create_by=request.user, steps = list(steps)) - for m in InputMaterial.objects.filter(subproduction=i, is_delete=False).order_by('sort'): + for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance) - for m in OutputMaterial.objects.filter(subproduction=i, is_delete=False).order_by('sort'): + for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance) production_plan.is_planed=True production_plan.save() From ac8bf48a91c6202531e117bdda0672d4a969bffa Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 Oct 2021 09:59:55 +0800 Subject: [PATCH 12/31] =?UTF-8?q?=E5=AD=90=E8=BF=9B=E5=BA=A6=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=8E=A5=E5=8F=A3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._alter_subproductionprogress_count_real.py | 18 ++++++++++ hb_server/apps/pm/models.py | 2 +- hb_server/apps/wf/migrations/0014_wfscript.py | 36 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 hb_server/apps/pm/migrations/0006_alter_subproductionprogress_count_real.py create mode 100644 hb_server/apps/wf/migrations/0014_wfscript.py diff --git a/hb_server/apps/pm/migrations/0006_alter_subproductionprogress_count_real.py b/hb_server/apps/pm/migrations/0006_alter_subproductionprogress_count_real.py new file mode 100644 index 0000000..a4924dd --- /dev/null +++ b/hb_server/apps/pm/migrations/0006_alter_subproductionprogress_count_real.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-10-19 01:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0005_auto_20211019_0944'), + ] + + operations = [ + migrations.AlterField( + model_name='subproductionprogress', + name='count_real', + field=models.IntegerField(default=0, verbose_name='实际出入数'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 418e4dc..8e9299d 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -62,4 +62,4 @@ class SubProductionProgress(BaseModel): material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) type = models.IntegerField('物料应用类型', default=1) count = models.IntegerField('应出入数') - count_real = models.IntegerField('实际出入数') + count_real = models.IntegerField('实际出入数', default=0) diff --git a/hb_server/apps/wf/migrations/0014_wfscript.py b/hb_server/apps/wf/migrations/0014_wfscript.py new file mode 100644 index 0000000..2b88441 --- /dev/null +++ b/hb_server/apps/wf/migrations/0014_wfscript.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.6 on 2021-10-19 01:58 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('wf', '0013_alter_ticketflow_transition'), + ] + + operations = [ + migrations.CreateModel( + name='WfScript', + fields=[ + ('id', models.BigAutoField(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='删除标记')), + ('usage', models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=1, verbose_name='脚本用途')), + ('wait', models.BooleanField(default=True, verbose_name='是否等待执行完成')), + ('name', models.CharField(max_length=100, verbose_name='脚本名称')), + ('content', models.TextField(verbose_name='脚本内容')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('workflow', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.workflow', verbose_name='关联工作流')), + ], + options={ + 'abstract': False, + }, + ), + ] From 1f21df79e5d78b2c62b1a403dbab3b3837773cdf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 Oct 2021 10:26:29 +0800 Subject: [PATCH 13/31] =?UTF-8?q?=E5=AD=90=E8=BF=9B=E5=BA=A6=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/pm/serializers.py | 3 ++- hb_server/apps/pm/views.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 7c79bc0..e117b8b 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -42,4 +42,5 @@ class GenSubPlanSerializer(serializers.Serializer): class SubProductionProgressSerializer(serializers.ModelSerializer): material_ = MaterialSimpleSerializer(source='material', read_only=True) class Meta: - model = SubProductionProgress \ No newline at end of file + model = SubProductionProgress + fields = '__all__' \ No newline at end of file diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index d50ffae..f869c2b 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -108,6 +108,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True) return Response(serializer.data) + class ResourceViewSet(GenericViewSet): perms_map = {'*': '*'} From 6ecc0f6e57e9f9e0b62a666b366aab2b6e8b57a7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 Oct 2021 11:18:32 +0800 Subject: [PATCH 14/31] =?UTF-8?q?=E5=B7=A5=E5=8D=95=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E8=81=9A=E5=90=88=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/pm/views.py | 15 ++++++++++++++- hb_server/apps/wf/views.py | 14 +++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index f869c2b..e8c35cb 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -104,11 +104,24 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer) def progress(self, request, pk=None): + """ + 生产进度详情 + """ obj = self.get_object() serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True) return Response(serializer.data) - + @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) + def issue(self, request, pk=None): + """ + 下达任务 + """ + obj = self.get_object() + if obj.state == 0: + obj.state = 1 + obj.save() + return Response() + raise APIException('计划状态有误') class ResourceViewSet(GenericViewSet): perms_map = {'*': '*'} diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 3d3f439..a86e3f3 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -1,3 +1,4 @@ +from django.db.models import query from apps.system.models import User from apps.wf.filters import TicketFilterSet from django.core.exceptions import AppRegistryNotReady @@ -13,7 +14,7 @@ from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, from apps.wf.services import WfService from rest_framework.exceptions import APIException, PermissionDenied from rest_framework import status - +from django.db.models import Count # Create your views here. class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): perms_map = {'get': '*', 'post': 'workflow_create', @@ -165,6 +166,17 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin participant=ticket.create_by, transition=transition) return Response(TicketSerializer(instance=ticket).data) + @action(methods=['get'], detail=False, perms_map={'get':'*'}) + def duty_agg(self, request, pk=None): + """ + 工单待办聚合 + """ + ret = {} + queryset = Ticket.objects.filter(participant__contains=request.user.id, is_deleted=False)\ + .exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED]) + ret['total_count'] = queryset.count() + ret['details'] = list(queryset.annotate(count = Count('workflow')).values('workflow', 'workflow__name', 'count')) + return Response(ret) @action(methods=['post'], detail=True, perms_map={'post':'*'}) def handle(self, request, pk=None): From ba69ce62dee69740de69a0a143c99ef16e8de50f Mon Sep 17 00:00:00 2001 From: shijing Date: Tue, 19 Oct 2021 16:38:39 +0800 Subject: [PATCH 15/31] floatButton --- hb_client/src/App.vue | 20 ++++++++++++++- hb_client/src/api/testModel.js | 2 +- hb_client/src/api/workflow.js | 8 ++++++ hb_client/src/layout/components/Navbar.vue | 8 ++++++ hb_client/src/layout/index.vue | 28 ++++++++++++++++++++- hb_client/src/store/getters.js | 1 + hb_client/src/store/modules/user.js | 23 ++++++++++++++--- hb_client/src/views/dashboard/index.vue | 13 +++++++++- hb_client/src/views/login/index.vue | 2 ++ hb_client/src/views/testModel/faceLogin.vue | 22 ++++++++++++++-- 10 files changed, 118 insertions(+), 9 deletions(-) diff --git a/hb_client/src/App.vue b/hb_client/src/App.vue index b06679a..a1084ae 100644 --- a/hb_client/src/App.vue +++ b/hb_client/src/App.vue @@ -14,9 +14,19 @@ export default { }, data(){ return{ - isRouterAlive:true + isRouterAlive:true, + timer:null } }, + mounted(){ + this.$store.dispatch("user/getCount", {}) + this.timer = window.setInterval(() => { + setTimeout(() => { + this.$store.dispatch("user/getCount", {}) + },0) + },30000) + + }, methods:{ reload(){ this.isRouterAlive=false; @@ -24,6 +34,9 @@ export default { this.isRouterAlive=true; }) }, + }, + destroyed() { + clearInterval(this.timer) } } @@ -36,4 +49,9 @@ export default { .el-step__title.is-process{ color: #409EFF; } + .navbarBadge .el-badge__content.is-fixed{ + top: 15px; + right: 18px; + } + diff --git a/hb_client/src/api/testModel.js b/hb_client/src/api/testModel.js index fc6204e..3cbdb33 100644 --- a/hb_client/src/api/testModel.js +++ b/hb_client/src/api/testModel.js @@ -1,7 +1,7 @@ import request from '@/utils/request' export function faceLogin(data) { return request({ - url: '/system/facelogin/', + url: '/hrm/facelogin/', method: 'post', data }) diff --git a/hb_client/src/api/workflow.js b/hb_client/src/api/workflow.js index 069f700..a3a2225 100644 --- a/hb_client/src/api/workflow.js +++ b/hb_client/src/api/workflow.js @@ -225,3 +225,11 @@ export function getTicketFlowlog(id) { method: 'get' }) } +//工单代办数量 +export function getCount(data) { + return request({ + url: `/wf/ticket/duty_agg/`, + method: 'get', + params:data + }) +} diff --git a/hb_client/src/layout/components/Navbar.vue b/hb_client/src/layout/components/Navbar.vue index 7cb30fd..55113ad 100644 --- a/hb_client/src/layout/components/Navbar.vue +++ b/hb_client/src/layout/components/Navbar.vue @@ -6,6 +6,9 @@
@@ -15,6 +21,7 @@ @@ -92,4 +105,17 @@ export default { .mobile .fixed-header { width: 100%; } + .floatDiv{ + position: fixed; + z-index: 3000; + bottom: 10vh; + right: 5vh; + width: 50px; + height: 50px; + cursor: pointer; + text-align: center; + line-height: 50px; + border-radius: 26px; + border: 2px solid #409EFF; + } diff --git a/hb_client/src/store/getters.js b/hb_client/src/store/getters.js index 566e457..3ff47b0 100644 --- a/hb_client/src/store/getters.js +++ b/hb_client/src/store/getters.js @@ -5,6 +5,7 @@ const getters = { avatar: state => state.user.avatar, name: state => state.user.name, perms: state => state.user.perms, + count: state => state.user.count, size: state => state.app.size, permission_routes: state => state.permission.routes, visitedViews: state => state.tagsView.visitedViews, diff --git a/hb_client/src/store/modules/user.js b/hb_client/src/store/modules/user.js index 0f1d024..1ebc241 100644 --- a/hb_client/src/store/modules/user.js +++ b/hb_client/src/store/modules/user.js @@ -1,4 +1,5 @@ import { login, logout, getInfo } from '@/api/user' +import { getCount } from '@/api/workflow' import { getToken, setToken, removeToken } from '@/utils/auth' import { resetRouter } from '@/router' @@ -7,6 +8,7 @@ const getDefaultState = () => { token: getToken(), name: '', avatar: '', + count: '', perms: [] } } @@ -28,6 +30,9 @@ const mutations = { }, SET_PERMS: (state, perms) => { state.perms = perms + }, + SET_COUNT: (state, count) => { + state.count = count } } @@ -90,15 +95,27 @@ const actions = { }, // remove token - resetToken({ commit }) { + resetToken({ commit },data) { return new Promise(resolve => { - removeToken() // must remove token first - commit('RESET_STATE') + removeToken(); // must remove token first + commit('RESET_STATE'); + commit('SET_TOKEN', data.access); + setToken(data.access); resolve() }) }, setSize({ commit }, size) { commit('SET_SIZE', size) + }, + getCount({ commit }) { + return new Promise((resolve, reject) => { + getCount({}).then((res) => { + commit('SET_COUNT', res.data.total_count); + resolve() + }).catch(error => { + reject(error) + }) + }) } } diff --git a/hb_client/src/views/dashboard/index.vue b/hb_client/src/views/dashboard/index.vue index bfd075e..5690923 100644 --- a/hb_client/src/views/dashboard/index.vue +++ b/hb_client/src/views/dashboard/index.vue @@ -1,5 +1,9 @@