From 548159edfe920370eb74bc11eb74c46ff2341e6d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Aug 2020 09:51:18 +0800 Subject: [PATCH 1/6] vue index --- .../migrations/0019_auto_20200813_1744.py | 32 +++++++++++++++++++ server/apps/project/models.py | 4 +-- server/apps/project/serializers.py | 5 +++ server/apps/project/urls.py | 1 + server/apps/project/views.py | 12 ++++++- 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 server/apps/project/migrations/0019_auto_20200813_1744.py diff --git a/server/apps/project/migrations/0019_auto_20200813_1744.py b/server/apps/project/migrations/0019_auto_20200813_1744.py new file mode 100644 index 0000000..0b9568e --- /dev/null +++ b/server/apps/project/migrations/0019_auto_20200813_1744.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.7 on 2020-08-13 09:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0018_certapp_level'), + ] + + operations = [ + migrations.RemoveField( + model_name='unit', + name='factory', + ), + migrations.RemoveField( + model_name='unit', + name='factory_v', + ), + migrations.RemoveField( + model_name='unit', + name='subapplication', + ), + migrations.AddField( + model_name='unit', + name='certapp', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='project.CertApp', verbose_name='所属业务'), + preserve_default=False, + ), + ] diff --git a/server/apps/project/models.py b/server/apps/project/models.py index 888ec8d..e4b99ad 100644 --- a/server/apps/project/models.py +++ b/server/apps/project/models.py @@ -159,12 +159,10 @@ class Unit(CommonBModel): """ 认证单元,一个单元一张证书 """ - factory = models.ForeignKey(Enterprise, on_delete=models.DO_NOTHING) - factory_v = JSONField(verbose_name='生产厂', blank=True) name = models.CharField('单元名称', max_length=200) description = models.TextField('单元描述', blank=True) unittype = models.ForeignKey(UnitType, verbose_name='单元类型', on_delete = models.SET_NULL, null=True, blank=True) - subapplication = models.ForeignKey(Application, verbose_name='所属子申请', on_delete = models.CASCADE) + certapp = models.ForeignKey(CertApp, verbose_name='所属业务', on_delete = models.CASCADE) class Meta: verbose_name = '认证单元' diff --git a/server/apps/project/serializers.py b/server/apps/project/serializers.py index a87a020..2898f8a 100644 --- a/server/apps/project/serializers.py +++ b/server/apps/project/serializers.py @@ -43,4 +43,9 @@ class CertappSerializer(serializers.ModelSerializer): create_by_ = UserListSerializer(source='create_by', read_only=True) class Meta: model = CertApp + fields = '__all__' + +class UnitSerializer(serializers.ModelSerializer): + class Meta: + model = Unit fields = '__all__' \ No newline at end of file diff --git a/server/apps/project/urls.py b/server/apps/project/urls.py index bdd946f..1a101e0 100644 --- a/server/apps/project/urls.py +++ b/server/apps/project/urls.py @@ -6,6 +6,7 @@ router = routers.DefaultRouter() router.register('application', ApplicationViewSet, basename="application") router.register('subapplication', SubApplicationViewSet, basename="subapplication") router.register('certapp', CertappViewset, basename="certapp") +router.register('unit', UnitViewSet, basename="unit") urlpatterns = [ path('', include(router.urls)) ] \ No newline at end of file diff --git a/server/apps/project/views.py b/server/apps/project/views.py index 26c2ac6..7416146 100644 --- a/server/apps/project/views.py +++ b/server/apps/project/views.py @@ -1,5 +1,6 @@ from django.shortcuts import render import rest_framework +from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.response import Response from rest_framework import status @@ -79,4 +80,13 @@ class CertappViewset(CreateUpdateCustomMixin, ModelViewSet): serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) \ No newline at end of file + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + +class UnitViewSet(CreateUpdateCustomMixin, ModelViewSet): + """ + 产品单元 + """ + perms_map = {'get': 'certapp_view', 'post':'certapp_create', 'put':'certapp_update','delete': 'certapp_delete'} + queryset = Unit.objects.all() + serializer_class = UnitSerializer + ordering = ['-create_time'] \ No newline at end of file From d30aac24af30efa36c964b7ab8d19a905becacaa Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Aug 2020 09:56:42 +0800 Subject: [PATCH 2/6] vue index --- client/src/api/unit.js | 40 ++++++ client/src/router/index.js | 14 +- client/src/views/certapp/certappform.vue | 8 +- client/src/views/certapp/productunit.vue | 136 ++++++++++++++++++++ client/src/views/implementrule/unittype.vue | 4 +- client/src/views/standard/standard.vue | 13 ++ 6 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 client/src/api/unit.js create mode 100644 client/src/views/certapp/productunit.vue diff --git a/client/src/api/unit.js b/client/src/api/unit.js new file mode 100644 index 0000000..6d09c5b --- /dev/null +++ b/client/src/api/unit.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +export function getUnitList(query) { + return request({ + url: '/project/unit/', + method: 'get', + params: query + }) +} + +export function createUnit(data) { + return request({ + url: '/project/unit/', + method: 'post', + data + }) +} + +export function deleteUnit(id) { + return request({ + url: `/project/unit/${id}/`, + method: 'delete' + }) +} + + +export function updateUnit(id, data) { + return request({ + url: `/project/unit/${id}/`, + method: 'put', + data + }) +} + +export function getUnit(id) { + return request({ + url: `/project/unit/${id}/`, + method: 'get', + }) +} diff --git a/client/src/router/index.js b/client/src/router/index.js index 1e70e48..83c039b 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -64,16 +64,16 @@ export const asyncRoutes = [ { path: '/project', component: Layout, - redirect: '/project/case', + redirect: '/project/certapp', name: 'ProjectManage', meta: { title: '认证项目', icon: 'example'}, children: [ - { - path: 'application', - name: 'Applicaion', - component: () => import('@/views/application/application'), - meta: { title: '认证受理', icon: 'example', perms: ['application_view'] } - }, + // { + // path: 'application', + // name: 'Applicaion', + // component: () => import('@/views/application/application'), + // meta: { title: '认证受理', icon: 'example', perms: ['application_view'] } + // }, { path: 'certapp', name: 'Certapp', diff --git a/client/src/views/certapp/certappform.vue b/client/src/views/certapp/certappform.vue index 3b06059..c3be79f 100644 --- a/client/src/views/certapp/certappform.vue +++ b/client/src/views/certapp/certappform.vue @@ -1,11 +1,11 @@ diff --git a/client/src/views/implementrule/unittype.vue b/client/src/views/implementrule/unittype.vue index d1e4838..25b08aa 100644 --- a/client/src/views/implementrule/unittype.vue +++ b/client/src/views/implementrule/unittype.vue @@ -21,7 +21,7 @@ - + + diff --git a/client/src/views/project/project copy.vue b/client/src/views/project/project copy.vue new file mode 100644 index 0000000..0d7877c --- /dev/null +++ b/client/src/views/project/project copy.vue @@ -0,0 +1,178 @@ + + diff --git a/client/src/views/project/project.vue b/client/src/views/project/project.vue new file mode 100644 index 0000000..f01ef1d --- /dev/null +++ b/client/src/views/project/project.vue @@ -0,0 +1,307 @@ + + From 328ea10969f0ce3a94536a839bf9a19c8931af70 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 24 Aug 2020 11:11:56 +0800 Subject: [PATCH 5/6] pagination --- client/src/App.vue | 11 +- client/src/components/Pagination/index.vue | 2 +- client/src/router/index.js | 4 +- client/src/views/certapp/cccform.vue | 25 +- client/src/views/certapp/certapp.vue | 8 +- client/src/views/certapp/certappform.vue | 21 +- client/src/views/plan/plan.vue | 332 +++++++++++++++++++++ client/src/views/project/project.vue | 4 +- server/apps/project/models.py | 3 - 9 files changed, 377 insertions(+), 33 deletions(-) create mode 100644 client/src/views/plan/plan.vue diff --git a/client/src/App.vue b/client/src/App.vue index f7e1c74..18867b6 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -7,13 +7,18 @@ .el-table--medium td,   .el-table--medium th { - padding: 0px 0; + padding: 2px 0; } .el-form-item { - margin-bottom: 10px; + margin-bottom: 16px; } - +.el-card__body { + padding: 10px; +} +.el-card__header { + padding: 10px; +} diff --git a/client/src/views/project/project.vue b/client/src/views/project/project.vue index f01ef1d..2640fea 100644 --- a/client/src/views/project/project.vue +++ b/client/src/views/project/project.vue @@ -3,7 +3,7 @@
- 新建项目 + 新建项目
- 生成项目 + 生成项目
diff --git a/client/src/views/plan/handle.vue b/client/src/views/plan/handle.vue new file mode 100644 index 0000000..d9ac205 --- /dev/null +++ b/client/src/views/plan/handle.vue @@ -0,0 +1,528 @@ + + + \ No newline at end of file diff --git a/client/src/views/plan/paichai.vue b/client/src/views/plan/paichai.vue new file mode 100644 index 0000000..f8cb32d --- /dev/null +++ b/client/src/views/plan/paichai.vue @@ -0,0 +1,220 @@ + + diff --git a/client/src/views/plan/plan.vue b/client/src/views/plan/plan.vue index 168602a..0fb054d 100644 --- a/client/src/views/plan/plan.vue +++ b/client/src/views/plan/plan.vue @@ -1,265 +1,290 @@ diff --git a/client/src/views/project/project.vue b/client/src/views/project/project.vue index 2640fea..22df5f8 100644 --- a/client/src/views/project/project.vue +++ b/client/src/views/project/project.vue @@ -87,7 +87,7 @@ + @@ -184,6 +191,8 @@ export default { certappData: [], listLoading: true, listQuery: { + page:1, + page_size:20, status: "已受理", }, listQuery_project: { diff --git a/client/src/views/system/dict.vue b/client/src/views/system/dict.vue index dd15dae..e001527 100644 --- a/client/src/views/system/dict.vue +++ b/client/src/views/system/dict.vue @@ -89,14 +89,14 @@ icon="el-icon-edit" @click="handleEdit(scope)" /> - + /> @@ -297,7 +297,7 @@ export default { .then(async() => { const { code } = await deleteDict(scope.row.id) if (code>=200){ - this.dictList.splice(scope.row.index, 1) + this.getList() this.$message({ type: 'success', message: '成功删除!' diff --git a/server/apps/crm/serializers.py b/server/apps/crm/serializers.py index e7623b1..42d0b5c 100644 --- a/server/apps/crm/serializers.py +++ b/server/apps/crm/serializers.py @@ -4,13 +4,18 @@ from .models import Enterprise,EnterpriseAddress from apps.system.serializers import DictSerializer -class EnterpriseSerializer(serializers.ModelSerializer): + + +class EnterpriseAddressSerializer(serializers.ModelSerializer): class Meta: - model = Enterprise + model = EnterpriseAddress fields = '__all__' + + class EnterpriseListSerializer(serializers.ModelSerializer): type = DictSerializer() economy_class = DictSerializer() + class Meta: model = Enterprise fields = ['id','query_code', 'code', 'name','ename','type','legal','build_time','person_count','ceramics_output','gassets','linkman1_name','linkman1_tel','linkman1_mobile','country_code','parent','credit_code','professional','all_person','economy_class','economy_type','linkman1_duty','business_type'] @@ -18,14 +23,16 @@ class EnterpriseListSerializer(serializers.ModelSerializer): def setup_eager_loading(queryset): """ Perform necessary eager loading of data. """ queryset = queryset.select_related('type','economy_class') + queryset = queryset.prefetch_related('enterpriseaddress_enterprise',) return queryset - - def get_cert_type(self, obj): - return obj.get_cert_type_display() -class EnterpriseAddressSerializer(serializers.ModelSerializer): + +class EnterpriseSerializer(serializers.ModelSerializer): + address_ = EnterpriseAddressSerializer(source='enterpriseaddress_enterprise', many=True) class Meta: - model = EnterpriseAddress + model = Enterprise fields = '__all__' - - - \ No newline at end of file + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + # queryset = queryset.select_related('type','economy_class') + queryset = queryset.prefetch_related('enterpriseaddress_enterprise',) + return queryset \ No newline at end of file diff --git a/server/apps/employee/serializers.py b/server/apps/employee/serializers.py index b292e23..6d45210 100644 --- a/server/apps/employee/serializers.py +++ b/server/apps/employee/serializers.py @@ -1,3 +1,4 @@ +from utils import serializer from rest_framework import serializers from .models import * @@ -5,6 +6,8 @@ from .models import * from apps.system.serializers import DictSerializer, UserListSerializer from apps.certset.serializers import ImplementRuleSerializer + + class EmployeeSerializer(serializers.ModelSerializer): """ 普通序列化 @@ -24,6 +27,41 @@ class EmployeeSerializer(serializers.ModelSerializer): return queryset + +class TeamMemberSerializer(serializers.ModelSerializer): + """ + 选人序列化 + """ + user_ = UserListSerializer(source='user', read_only=True) + majors = serializers.SerializerMethodField() + + class Meta: + model = Employee + fields = ['code', 'user', 'user_', 'photo', 'fields', 'majors'] + + def get_majors(self, obj): + majors = obj.ability_employee.all() + m = [] + for i in majors: + n = {'major':'', 'abilitys':''} + if not i.is_paused: + if i.major: + n['major'] = i.major.code + n['abilitys'] = i.auditor_abilitys.values_list('name', flat=True) + m.append(n) + elif i.major_rule: + n['major'] = i.major_rule.code + n['abilitys'] = i.auditor_abilitys.values_list('name', flat=True) + m.append(n) + return m + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('user') + return queryset + + class QualificationSerializer(serializers.ModelSerializer): """ 普通序列化 diff --git a/server/apps/employee/urls.py b/server/apps/employee/urls.py index b34b8ae..4d0e30e 100644 --- a/server/apps/employee/urls.py +++ b/server/apps/employee/urls.py @@ -9,5 +9,7 @@ router.register('ability', AbilityViewSet, basename="ability") router.register('education', EducationViewSet, basename="education") router.register('train', TrainViewSet, basename="train") urlpatterns = [ + path('employee/team/', TeamMemberView.as_view()), path('', include(router.urls)) + ] \ No newline at end of file diff --git a/server/apps/employee/views.py b/server/apps/employee/views.py index 77ce3a5..2696ef0 100644 --- a/server/apps/employee/views.py +++ b/server/apps/employee/views.py @@ -1,4 +1,5 @@ from django.shortcuts import render +from rest_framework.generics import ListAPIView from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from rest_framework import status @@ -7,6 +8,17 @@ from .serializers import * from apps.system.permission_data import RbacFilterSet from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin # Create your views here. + + +class TeamMemberView(ListAPIView): + perms_map = {'get': '*'} + queryset = Employee.objects.all() + serializer_class = TeamMemberSerializer + filterset_fields = ['is_onjob', 'is_fulltime', 'user__dept'] + search_fields = ['code', 'remember_code', 'user__name', 'fields', 'ability_employee__major__code', 'ability_employee__major_rule__code'] + ordering = ['-pk'] + + class EmployeeViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): """ 详细信息-增删改查 @@ -17,7 +29,7 @@ class EmployeeViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): serializer_class = EmployeeSerializer search_fields = ['code', 'remember_code', 'user__name', 'fields'] filterset_fields = ['is_onjob', 'is_fulltime', 'user__dept'] - ordering = ['-create_time'] + ordering = ['-pk'] class QualificationViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): diff --git a/server/apps/plan/__init__.py b/server/apps/plan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/admin.py b/server/apps/plan/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/server/apps/plan/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/server/apps/plan/apps.py b/server/apps/plan/apps.py new file mode 100644 index 0000000..7cf7268 --- /dev/null +++ b/server/apps/plan/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PlanConfig(AppConfig): + name = 'plan' diff --git a/server/apps/plan/filters.py b/server/apps/plan/filters.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/migrations/0001_initial.py b/server/apps/plan/migrations/0001_initial.py new file mode 100644 index 0000000..4c6f68f --- /dev/null +++ b/server/apps/plan/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('system', '0031_delete_bscodeset'), + ('project', '0026_merge_20200825_1636'), + ] + + operations = [ + migrations.CreateModel( + name='Plan', + 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(max_length=1000, verbose_name='计划名称')), + ('month', models.DateField(blank=True, null=True, verbose_name='计划审核月份')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_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='plan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '审核计划', + 'verbose_name_plural': '审核计划', + }, + ), + migrations.CreateModel( + name='ContactRecord', + 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='删除标记')), + ('contact_date', models.DateField(verbose_name='联系时间')), + ('content', models.TextField(verbose_name='内容')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('certapp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contactrecord_certapp', to='project.CertApp', verbose_name='关联申请')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contactrecord_project', to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '联系记录', + 'verbose_name_plural': '联系记录', + }, + ), + ] diff --git a/server/apps/plan/migrations/0002_auto_20200901_1511.py b/server/apps/plan/migrations/0002_auto_20200901_1511.py new file mode 100644 index 0000000..ce9aea2 --- /dev/null +++ b/server/apps/plan/migrations/0002_auto_20200901_1511.py @@ -0,0 +1,43 @@ +# Generated by Django 3.0.7 on 2020-09-01 07:11 + +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 = [ + ('system', '0031_delete_bscodeset'), + ('project', '0027_auto_20200825_1636'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('plan', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='plan', + name='name', + field=models.CharField(max_length=1000, unique=True, verbose_name='计划名称'), + ), + migrations.CreateModel( + name='Team', + 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='删除标记')), + ('is_leader', models.BooleanField(default=False, verbose_name='是否是组长')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('identity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.Dict', verbose_name='身份')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='成员')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/server/apps/plan/migrations/0003_auto_20200901_1555.py b/server/apps/plan/migrations/0003_auto_20200901_1555.py new file mode 100644 index 0000000..90cdd35 --- /dev/null +++ b/server/apps/plan/migrations/0003_auto_20200901_1555.py @@ -0,0 +1,42 @@ +# Generated by Django 3.0.7 on 2020-09-01 07:55 + +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 = [ + ('system', '0031_delete_bscodeset'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('project', '0027_auto_20200825_1636'), + ('plan', '0002_auto_20200901_1511'), + ] + + operations = [ + migrations.CreateModel( + name='Member', + 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='删除标记')), + ('is_leader', models.BooleanField(default=False, verbose_name='是否是组长')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('identity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_identity', to='system.Dict', verbose_name='身份')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_project', to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='member_user', to=settings.AUTH_USER_MODEL, verbose_name='成员')), + ], + options={ + 'verbose_name': '审核组成员', + 'verbose_name_plural': '审核组成员', + }, + ), + migrations.DeleteModel( + name='Team', + ), + ] diff --git a/server/apps/plan/migrations/__init__.py b/server/apps/plan/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/models.py b/server/apps/plan/models.py new file mode 100644 index 0000000..419f9e9 --- /dev/null +++ b/server/apps/plan/models.py @@ -0,0 +1,57 @@ +from utils import model +from django.contrib.postgres.fields import JSONField +from django.db import models +from rest_framework.exceptions import ParseError +from simple_history.models import HistoricalRecords + +from apps.system.models import CommonAModel, CommonBModel, Dict, User +from apps.project.models import Project, CertApp +# Create your models here. + +class Plan(CommonBModel): + """ + 计划(项目组) + """ + name = models.CharField('计划名称', max_length = 1000, unique=True) + month = models.DateField('计划审核月份', null=True, blank=True) + + class Meta: + verbose_name = '审核计划' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + +class ContactRecord(CommonBModel): + """ + 联系记录 + """ + project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='所属项目', related_name='contactrecord_project') + certapp = models.ForeignKey(CertApp, on_delete=models.CASCADE, verbose_name='关联申请', null=True, blank=True, related_name='contactrecord_certapp') + contact_date = models.DateField('联系时间') + content = models.TextField('内容') + + class Meta: + verbose_name = '联系记录' + verbose_name_plural = verbose_name + + def __str__(self): + return self.content + +class Member(CommonBModel): + """ + 审核组成员 + """ + user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='成员', related_name='member_user') + is_leader = models.BooleanField('是否是组长', default=False) + project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='所属项目', related_name='member_project') + identity = models.ForeignKey(Dict, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='身份', related_name='member_identity') + + class Meta: + verbose_name = '审核组成员' + verbose_name_plural = verbose_name + + def __str__(self): + return self.member.name + + diff --git a/server/apps/plan/serializers.py b/server/apps/plan/serializers.py new file mode 100644 index 0000000..a5a083f --- /dev/null +++ b/server/apps/plan/serializers.py @@ -0,0 +1,24 @@ +from rest_framework import serializers + +from .models import * + +from apps.system.serializers import DictSerializer, UserListSerializer +from apps.certset.serializers import StandardSerializer + +class ContactRecordSerializer(serializers.ModelSerializer): + create_by_=UserListSerializer(source='create_by', read_only=True) + class Meta: + model = ContactRecord + fields = '__all__' + +class PlanSerializer(serializers.ModelSerializer): + class Meta: + model = Plan + fields = '__all__' + +class MemberSerializer(serializers.ModelSerializer): + user_ = UserListSerializer(source='user', read_only=True) + identity_ = DictSerializer(source='identity', read_only=True) + class Meta: + model = Member + fields = '__all__' \ No newline at end of file diff --git a/server/apps/plan/tests.py b/server/apps/plan/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/server/apps/plan/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/server/apps/plan/urls.py b/server/apps/plan/urls.py new file mode 100644 index 0000000..e98f8ab --- /dev/null +++ b/server/apps/plan/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, include +from .views import * +from rest_framework import routers + +router = routers.DefaultRouter() +router.register('contactrecord', ContactRecordViewSet, basename="contactrecord") +router.register('plan', PlanViewSet, basename='plan') +router.register('member', MemberViewSet, basename='member') +urlpatterns = [ + path('', include(router.urls)) +] \ No newline at end of file diff --git a/server/apps/plan/views.py b/server/apps/plan/views.py new file mode 100644 index 0000000..fd315c2 --- /dev/null +++ b/server/apps/plan/views.py @@ -0,0 +1,55 @@ +from django.shortcuts import render +import rest_framework +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet, GenericViewSet +from rest_framework.response import Response +from rest_framework import status +from .models import * +from .serializers import * +from apps.system.models import Dict +from apps.system.permission_data import RbacFilterSet +from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin +import random +from rest_framework.decorators import action +from .filters import * +from utils.pagination import PageOrNot +# Create your views here. + +class ContactRecordViewSet(CreateUpdateCustomMixin, PageOrNot, ModelViewSet): + """ + 联系记录 + """ + perms_map = {'get': 'contactrecord_view', 'post':'contactrecord_create', 'put':'contactrecord_update','delete': 'contactrecord_delete'} + queryset = ContactRecord.objects.all() + serializer_class = ContactRecordSerializer + ordering = ['pk'] + filterset_fields = ['project', 'certapp'] + +class PlanViewSet(RbacFilterSet, PageOrNot, ModelViewSet): + """ + 计划 + """ + perms_map = {'get': 'plan_view', 'post':'plan_create', 'put':'plan_update','delete': 'plan_delete'} + queryset = Plan.objects.all() + serializer_class = PlanSerializer + ordering = ['pk'] + # filterset_fields = ['project', 'certapp'] + +class MemberViewSet(CreateUpdateCustomMixin, PageOrNot, ModelViewSet): + """ + 审核组成员 + """ + perms_map = {'get': '*', 'post':'member_create', 'put':'member_update','delete': 'member_delete'} + queryset = Member.objects.all() + serializer_class = MemberSerializer + ordering = ['pk'] + filterset_fields = ['project'] + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + if Member.objects.filter(user = request.data['user'], project=request.data['project'], is_deleted=False).exists(): + return Response('已存在该成员', status= status.HTTP_400_BAD_REQUEST) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) \ No newline at end of file diff --git a/server/apps/project/filters.py b/server/apps/project/filters.py index 450b007..9a70524 100644 --- a/server/apps/project/filters.py +++ b/server/apps/project/filters.py @@ -5,4 +5,10 @@ class CertAppFilter(filters.FilterSet): noproject = filters.BooleanFilter(field_name='project', lookup_expr='isnull') class Meta: model = CertApp - fields = ['status', 'noproject'] \ No newline at end of file + fields = ['status', 'noproject'] + +class ProjectFilter(filters.FilterSet): + planoff = filters.BooleanFilter(field_name='plan', lookup_expr='isnull') + class Meta: + model = Project + fields = ['plan', 'planoff'] \ No newline at end of file diff --git a/server/apps/project/migrations/0026_merge_20200825_1636.py b/server/apps/project/migrations/0026_merge_20200825_1636.py new file mode 100644 index 0000000..da9b564 --- /dev/null +++ b/server/apps/project/migrations/0026_merge_20200825_1636.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0022_auto_20200821_1435'), + ('project', '0025_auto_20200821_1541'), + ] + + operations = [ + ] diff --git a/server/apps/project/migrations/0027_auto_20200825_1636.py b/server/apps/project/migrations/0027_auto_20200825_1636.py new file mode 100644 index 0000000..c2ec3a4 --- /dev/null +++ b/server/apps/project/migrations/0027_auto_20200825_1636.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('plan', '0001_initial'), + ('project', '0026_merge_20200825_1636'), + ] + + operations = [ + migrations.AddField( + model_name='certapp', + name='accept_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='受理人'), + ), + migrations.AlterField( + model_name='project', + name='plan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='plan.Plan', verbose_name='所属计划'), + ), + migrations.DeleteModel( + name='Plan', + ), + ] diff --git a/server/apps/project/models.py b/server/apps/project/models.py index 7bcc4c3..603b75b 100644 --- a/server/apps/project/models.py +++ b/server/apps/project/models.py @@ -7,23 +7,10 @@ from simple_history.models import HistoricalRecords from apps.certset.models import ImplementRule, UnitType, EvaluationItem, Standard from apps.crm.models import Enterprise from apps.system.models import CommonAModel, CommonBModel, Dict, User +# from apps.plan.models import Plan # Create your models here. -class Plan(CommonBModel): - """ - 计划(项目组) - """ - name = models.CharField('计划名称', max_length = 1000) - month = models.DateField('计划审核月份', null=True, blank=True) - - class Meta: - verbose_name = '审核计划' - verbose_name_plural = verbose_name - - def __str__(self): - return self.name - class Project(CommonBModel): """ 认证项目 @@ -43,7 +30,7 @@ class Project(CommonBModel): assign_date = models.DateField('下达日期', null=True, blank=True) assign_by = models.ForeignKey(User, verbose_name='下达人', on_delete=models.SET_NULL, null=True, blank=True) can_paichai = models.BooleanField('是否可派差', default = False) - plan = models.ForeignKey(Plan, verbose_name='所属计划', on_delete=models.SET_NULL, null=True, blank=True) + plan = models.ForeignKey('plan.Plan', verbose_name='所属计划', on_delete=models.SET_NULL, null=True, blank=True) class Meta: @@ -53,6 +40,7 @@ class Project(CommonBModel): def __str__(self): return self.number + class CertApp(CommonBModel): """ 认证受理 @@ -74,7 +62,7 @@ class CertApp(CommonBModel): number = models.CharField('受理编号', max_length = 100, null=True, blank=True) apply_date = models.DateField('申请日期', null=True, blank=True) accept_date = models.DateField('受理日期', null=True, blank=True) - + accept_by = models.ForeignKey(User, verbose_name='受理人', on_delete=models.SET_NULL, null=True, blank=True) applicant_v = JSONField(verbose_name='申请方') applicant = models.ForeignKey(Enterprise, related_name='certapp_applicant', on_delete=models.DO_NOTHING) diff --git a/server/apps/project/serializers.py b/server/apps/project/serializers.py index b7d40e7..14d0511 100644 --- a/server/apps/project/serializers.py +++ b/server/apps/project/serializers.py @@ -1,9 +1,11 @@ +from apps.plan.models import Plan from rest_framework import serializers from .models import * from apps.system.serializers import DictSerializer, UserListSerializer from apps.certset.serializers import StandardSerializer +from apps.plan.serializers import PlanSerializer class ApplicationCreateSerializer(serializers.ModelSerializer): number = serializers.CharField(required=False) @@ -41,9 +43,17 @@ class CertappSerializer(serializers.ModelSerializer): cccpv_class_ = DictSerializer(source='cccpv_class' , read_only=True) cnas_scopes_ = DictSerializer(source='cnas_scopes', many=True , read_only=True) create_by_ = UserListSerializer(source='create_by', read_only=True) + accept_by_ = UserListSerializer(source='accept_by', read_only=True) class Meta: model = CertApp fields = '__all__' + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('cert_field', 'cccpv_class', 'create_by', 'accept_by') + queryset = queryset.prefetch_related('cnas_scopes',) + return queryset class UnitSerializer(serializers.ModelSerializer): standard_ = StandardSerializer(source='standard', read_only=True) @@ -53,7 +63,10 @@ class UnitSerializer(serializers.ModelSerializer): class ProjectSerializer(serializers.ModelSerializer): create_by_ = UserListSerializer(source='create_by', read_only=True) + assign_by_ = UserListSerializer(source='assign_by', read_only=True) certapps = serializers.SerializerMethodField() + plan_ = PlanSerializer(source='plan', read_only=True) + class Meta: model = Project fields = '__all__' @@ -62,4 +75,16 @@ class ProjectSerializer(serializers.ModelSerializer): certapps = [] for i in CertApp.objects.filter(is_deleted=False, project=obj): certapps.append(i.cert_field.code +'(' + i.cccpv_class.name +')') - return certapps \ No newline at end of file + return certapps + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('create_by', 'assign_by', 'plan') + return queryset + +class ProjectUpdateSerializer(serializers.ModelSerializer): + + class Meta: + model = Project + fields = ['remark', 'can_paichai'] \ No newline at end of file diff --git a/server/apps/project/views.py b/server/apps/project/views.py index 6c78d84..a47daf7 100644 --- a/server/apps/project/views.py +++ b/server/apps/project/views.py @@ -1,5 +1,6 @@ +from rest_framework.views import APIView +from apps import project from django.shortcuts import render -import rest_framework from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.response import Response @@ -12,8 +13,10 @@ from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin import random from rest_framework.decorators import action from .filters import * +from utils.pagination import PageOrNot +from rest_framework.exceptions import ParseError, NotAuthenticated # Create your views here. -class ApplicationViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): +class ApplicationViewSet(RbacFilterSet, ModelViewSet): """ 认证申请 """ @@ -31,7 +34,7 @@ class ApplicationViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): def perform_create(self, serializer): serializer.save(create_by = self.request.user, belong_dept=self.request.user.dept, number=random.randrange(1000,2000)) -class SubApplicationViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): +class SubApplicationViewSet(RbacFilterSet, ModelViewSet): """ 子认证申请 """ @@ -62,7 +65,7 @@ class SubApplicationViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet return None return self.paginator.paginate_queryset(queryset, self.request, view=self) -class CertappViewset(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): +class CertappViewset(PageOrNot, RbacFilterSet, ModelViewSet): """ 申请受理 """ @@ -72,14 +75,8 @@ class CertappViewset(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): ordering = ['-create_time'] filterset_fields = ['status', 'project'] # filterset_class = CertAppFilter - - def paginate_queryset(self, queryset): - - if ((not self.request.query_params.get('page', None)) and (self.request.query_params.get('status', None))) or (self.paginator is None): - return None - return self.paginator.paginate_queryset(queryset, self.request, view=self) - return queryset + def create(self, request, *args, **kwargs): postdata = request.data postdata['number'] = random.randrange(1000,2000) @@ -102,7 +99,7 @@ class CertappViewset(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): obj.save() return Response(status=status.HTTP_200_OK) -class UnitViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): +class UnitViewSet(RbacFilterSet, ModelViewSet): """ 产品单元 """ @@ -112,22 +109,33 @@ class UnitViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): ordering = ['pk'] filterset_fields = ['certapp'] -class ProjectViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): + + +class ProjectViewSet(RbacFilterSet, ModelViewSet): perms_map = {'get': 'project_view', 'post':'project_create', 'put':'project_update','delete': 'project_delete'} queryset = Project.objects.all() serializer_class = ProjectSerializer ordering = ['pk'] + filterset_class = ProjectFilter + + def get_serializer_class(self): + if self.action == 'update': + return ProjectUpdateSerializer + return ProjectSerializer def create(self, request, *args, **kwargs): postdata = request.data postdata['number'] = random.randrange(8000,9000) serializer = self.get_serializer(data=postdata) serializer.is_valid(raise_exception=True) - instance = serializer.save(create_by = self.request.user, belong_dept=self.request.user.dept) - if 'certapps' in postdata and postdata['certapps']: - CertApp.objects.filter(pk__in = postdata['certapps']).update(project=instance, status='进行中') - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + if self.request.user is not None: + instance = serializer.save(create_by = self.request.user, belong_dept=self.request.user.dept) + if 'certapps' in postdata and postdata['certapps']: + CertApp.objects.filter(pk__in = postdata['certapps']).update(project=instance, status='进行中') + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + else: + return NotAuthenticated() @action(methods=['put'], detail=True, perms_map={'put':'project_assgin'}, url_name='project_assgin') @@ -138,7 +146,20 @@ class ProjectViewSet(CreateUpdateCustomMixin, RbacFilterSet, ModelViewSet): obj = self.get_object() if obj.status == '待下达': obj.status = '待派差' + obj.assgin_by = request.user obj.save() return Response(status=status.HTTP_200_OK) else: - return Response('状态有误', status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + return Response('状态有误', status=status.HTTP_400_BAD_REQUEST) + + @action(methods=['put'], detail=False, perms_map={'put':'project_plan'}, + url_name='project_plan') + def plan(self, request, *args, **kwargs): + """ + 项目计划 + """ + projects = request.data['projects'] + plan = request.data['plan'] + if projects: + Project.objects.filter(pk__in=projects).update(plan=plan) + return Response(status=status.HTTP_200_OK) \ No newline at end of file diff --git a/server/apps/system/permission_data.py b/server/apps/system/permission_data.py index e5550d9..c418be2 100644 --- a/server/apps/system/permission_data.py +++ b/server/apps/system/permission_data.py @@ -1,11 +1,11 @@ from django.db.models import Q from django.db.models.query import QuerySet from rest_framework.generics import GenericAPIView -from apps.system.mixins import CreateUpdateModelBMixin +from apps.system.mixins import CreateUpdateModelBMixin, CreateUpdateCustomMixin from utils.queryset import get_child_queryset2 -class RbacFilterSet(CreateUpdateModelBMixin, object): +class RbacFilterSet(CreateUpdateCustomMixin, object): """ 数据权限控权返回的queryset 在必须的View下继承 @@ -21,8 +21,6 @@ class RbacFilterSet(CreateUpdateModelBMixin, object): ) queryset = self.queryset - if hasattr(self.get_serializer_class(), 'setup_eager_loading'): - queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() @@ -51,6 +49,8 @@ class RbacFilterSet(CreateUpdateModelBMixin, object): elif '仅本人' in data_range: queryset = queryset.filter(Q(create_by=user)|Q(update_by=user)) return queryset + if hasattr(self.get_serializer_class(), 'setup_eager_loading'): + queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 return queryset diff --git a/server/server/settings.py b/server/server/settings.py index b7c853e..3aadaed 100644 --- a/server/server/settings.py +++ b/server/server/settings.py @@ -49,7 +49,8 @@ INSTALLED_APPS = [ 'apps.certset', 'apps.employee', 'apps.project', - 'apps.laboratory' + 'apps.laboratory', + 'apps.plan' ] MIDDLEWARE = [ diff --git a/server/server/urls.py b/server/server/urls.py index 35ecda9..c87268e 100644 --- a/server/server/urls.py +++ b/server/server/urls.py @@ -36,6 +36,7 @@ urlpatterns = [ path('crm/', include('apps.crm.urls')), path('employee/', include('apps.employee.urls')), path('project/', include('apps.project.urls')), + path('plan/', include('apps.plan.urls')), path('laboratory/', include('apps.laboratory.urls')), path('docs/', include_docs_urls(title="接口文档", authentication_classes=[], permission_classes=[])), diff --git a/server/utils/pagination.py b/server/utils/pagination.py index 72e6efa..675d8a8 100644 --- a/server/utils/pagination.py +++ b/server/utils/pagination.py @@ -1,6 +1,16 @@ from rest_framework.pagination import PageNumberPagination - +from rest_framework.exceptions import ParseError class MyPagination(PageNumberPagination): page_size = 10 page_size_query_param = 'page_size' + +class PageOrNot: + def paginate_queryset(self, queryset): + if (self.paginator is None): + return None + elif self.request.query_params.get('pageoff', None) and self.get_queryset().count()<500: + return None + elif self.request.query_params.get('pageoff', None) and self.get_queryset().count()>=500: + return ParseError('单次请求数据量大,请求中止') + return self.paginator.paginate_queryset(queryset, self.request, view=self) \ No newline at end of file