diff --git a/client/dist/index.html b/client/dist/index.html index efd420b..803ec8c 100644 --- a/client/dist/index.html +++ b/client/dist/index.html @@ -1 +1 @@ -vue Admin Template
\ No newline at end of file +vue Admin Template
\ No newline at end of file diff --git a/client/src/api/qualificationInfo.js b/client/src/api/qualificationInfo.js index e202f7c..2ebcc11 100644 --- a/client/src/api/qualificationInfo.js +++ b/client/src/api/qualificationInfo.js @@ -27,7 +27,7 @@ export function getQi(id) { export function updateQi(id, data) { return request({ - url: `/info/faq/${id}/`, + url: `/info/faqch/${id}/`, method: 'put', data }) diff --git a/client/src/api/role.js b/client/src/api/role.js index e25e097..a6563a0 100644 --- a/client/src/api/role.js +++ b/client/src/api/role.js @@ -14,6 +14,14 @@ export function getRoleAll() { }) } +export function getRoleList(params) { + return request({ + url: '/system/role/', + method: 'get', + params + }) +} + export function createRole(data) { return request({ url: '/system/role/', diff --git a/client/src/api/userexam.js b/client/src/api/userexam.js new file mode 100644 index 0000000..6cf0dbd --- /dev/null +++ b/client/src/api/userexam.js @@ -0,0 +1,51 @@ +import request from '@/utils/request' + + +export function getUserList(query) { + return request({ + url: '/system/userexam/', + method: 'get', + params: query + }) +} + + +export function createUser(data) { + return request({ + url: '/system/userexam/', + method: 'post', + data + }) +} + +export function updateUser(id, data) { + return request({ + url: `/system/userexam/${id}/`, + method: 'put', + data + }) +} + + +export function deleteUserExam(id, data) { + return request({ + url: `/system/userexam/${id}/`, + method: 'delete', + data + }) +} + +export function resetUserpw(id) { + return request({ + url: `/system/user/${id}/resetpw/`, + method: 'put', + }) +} + +export function changePassword(data) { + return request({ + url: '/system/user/password/', + method: 'put', + data + }) +} \ No newline at end of file diff --git a/client/src/router/index.js b/client/src/router/index.js index 2f67332..0327d44 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -559,20 +559,20 @@ export const asyncRoutes = [ component: Layout, redirect: '/exam/questions', name: 'exam', - meta: { title: '考试', icon: 'PT', perms: ['pt_view'] }, + meta: { title: '考试', icon: 'Exam', perms: ['Exam'] }, alwaysShow: true, children: [ { path: 'classify', name: '题目分类', component: () => import('@/views/exam/classify.vue'), - meta: { title: '题目分类', perms: ['pt_view'] } + meta: { title: '题目分类', perms: ['CateQues'] } }, { path: 'questions', name: '题目列表', component: () => import('@/views/exam/questions.vue'), - meta: { title: '题目列表', perms: ['pt_view'] } + meta: { title: '题目列表', perms: ['CateQues'] } }, { path: 'questionCreate', @@ -592,7 +592,7 @@ export const asyncRoutes = [ path: 'testPaper', name: '考试试卷', component: () => import('@/views/exam/testPaper.vue'), - meta: { title: '考试试卷', perms: ['pt_view'] } + meta: { title: '考试试卷', perms: ['Paper'] } }, { path: 'paperCreate', @@ -612,13 +612,19 @@ export const asyncRoutes = [ path: 'index', name: '考试', component: () => import('@/views/exam/index.vue'), - meta: { title: '考试', perms: ['pt_view'] } + meta: { title: '考试', perms: ['Paper'] } }, { path: 'record', name: '考试记录', component: () => import('@/views/exam/examRecord.vue'), - meta: { title: '考试记录', perms: ['pt_view'] } + meta: { title: '考试记录', perms: ['RecordExam'] } + }, + { + path: 'userExam', + name: '用户考试管理', + component: () => import('@/views/system/userExam.vue'), + meta: { title: '用户考试管理', perms: ['UserExam'] } }, ] }, diff --git a/client/src/views/informatiomCollect/qualiChange.vue b/client/src/views/informatiomCollect/qualiChange.vue index 584e9c2..d327eda 100644 --- a/client/src/views/informatiomCollect/qualiChange.vue +++ b/client/src/views/informatiomCollect/qualiChange.vue @@ -25,11 +25,11 @@ diff --git a/client/src/views/system/userExam.vue b/client/src/views/system/userExam.vue new file mode 100644 index 0000000..5f53197 --- /dev/null +++ b/client/src/views/system/userExam.vue @@ -0,0 +1,500 @@ + + + diff --git a/server/apps/exam/views.py b/server/apps/exam/views.py index b14d694..a946c09 100644 --- a/server/apps/exam/views.py +++ b/server/apps/exam/views.py @@ -20,6 +20,8 @@ from apps.exam.filters import ExamRecordFilter, ExamFilter from datetime import timedelta from apps.system.mixins import CreateUpdateCustomMixin from apps.edu.serializers import CertificateSerializer +from utils.queryset import get_child_queryset2 +from apps.system.permission import has_permission from datetime import datetime # Create your views here. @@ -354,7 +356,15 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G search_fields = ('create_by__name', 'create_by__username', 'exam__name', 'belong_dept__name') filterset_class = ExamRecordFilter - # 排序 + def get_queryset(self): + qs = super().get_queryset() + # if self.request.method == 'GET': + # return qs + # else: + # return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept)) + if has_permission('ability_review_jygl', self.request.user): + return qs + return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept)) def get_serializer_class(self): if self.action == 'retrieve': @@ -397,7 +407,6 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G 提交答卷 ''' er = self.get_object() - print('er----------------', er) now = timezone.now() if er.create_by != request.user: raise ParseError('提交人有误') diff --git a/server/apps/system/serializers.py b/server/apps/system/serializers.py index b5170cb..8ea4119 100644 --- a/server/apps/system/serializers.py +++ b/server/apps/system/serializers.py @@ -95,6 +95,7 @@ class UserListSerializer(serializers.ModelSerializer): queryset = queryset.prefetch_related('roles',) return queryset + class UserModifySerializer(serializers.ModelSerializer): """ 用户编辑序列化 diff --git a/server/apps/system/urls.py b/server/apps/system/urls.py index 9e3494f..325f84f 100644 --- a/server/apps/system/urls.py +++ b/server/apps/system/urls.py @@ -1,11 +1,12 @@ from django.urls import path, include -from .views import CityViewSet, ProviceViewSet, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, InitAreaView, sendMsg +from .views import CityViewSet, ProviceViewSet, UserExamViewset, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, InitAreaView, sendMsg from rest_framework import routers router = routers.DefaultRouter() router.register('user', UserViewSet, basename="user") +router.register('userexam', UserExamViewset, basename="userexam") router.register('organization', OrganizationViewSet, basename="organization") router.register('permission', PermissionViewSet, basename="permission") router.register('role', RoleViewSet, basename="role") diff --git a/server/apps/system/views.py b/server/apps/system/views.py index 8057ae8..1b8b0c8 100644 --- a/server/apps/system/views.py +++ b/server/apps/system/views.py @@ -45,26 +45,84 @@ import requests import json from rest_framework.exceptions import AuthenticationFailed from django.db import transaction -logger = logging.getLogger('log') -# logger.info('请求成功! response_code:{};response_headers:{};response_body:{}'.format(response_code, response_headers, response_body[:251])) -# logger.error('请求出错:{}'.format(error)) - - -class LogoutView(APIView): - permission_classes = [] - - def get(self, request, *args, **kwargs): # 可将token加入黑名单 - return Response(status=status.HTTP_200_OK) +from rest_framework.exceptions import ParseError +from apps.system.permission import has_permission +from openpyxl import load_workbook import random import smtplib import string from email.header import Header from email.mime.text import MIMEText from email.utils import formataddr - from rest_framework_simplejwt.tokens import RefreshToken +logger = logging.getLogger('log') +# logger.info('请求成功! response_code:{};response_headers:{};response_body:{}'.format(response_code, response_headers, response_body[:251])) +# logger.error('请求出错:{}'.format(error)) + + +class ImpMixin: + def get_queryset(self): + mydept = self.request.user.dept + qs = super().get_queryset() + if has_permission('task2', self.request.user): + return qs + return qs.filter(belong_dept=mydept) + + def format_date(self, ind, val): + new_val = val + if isinstance(val, datetime.datetime): + new_val = val.date() + elif isinstance(val, datetime.date): + new_val = val + elif isinstance(val, str): + try: + new_val = datetime.datetime.strptime(val, '%Y-%m-%d').date() + except ValueError: + raise ParseError(f'第{ind}行, 日期时间格式错误') + elif val is None: + pass + else: + raise ParseError(f'第{ind}行, 日期时间格式错误') + return new_val + + def get_enum(self, val, atuple, ind): + for i in atuple: + if i[1] == val: + return i[0] + raise ParseError('第{}: 请选择固定选项值'.format(ind)) + + def F(self, data, sheet, i, etype): + raise NotImplementedError() + + def gen_imp_view(self, request, start: int, mySerializer): + if 'file' not in request.data: + raise ParseError('请提供文件') + path = request.data['file'] + print(path, "---------ssss") + + if not str(path).endswith('.xlsx'): + raise ParseError('请提供xlsx格式文件') + fullpath = settings.BASE_DIR + str(path) + wb = load_workbook(fullpath,data_only=True) + sheet = wb.active + # 遍历Excel文件中的数据 + data_list = self.build_data(sheet, start) + serializer = mySerializer(data=data_list, many=True, context={'request': request}) + if serializer.is_valid(): + serializer.save(create_by=request.user, belong_dept=request.user.dept) + else: + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + return Response({'uploaded': 'File uploaded successfully'}, status=status.HTTP_201_CREATED) + + +class LogoutView(APIView): + permission_classes = [] + + def get(self, request, *args, **kwargs): # 可将token加入黑名单 + return Response(status=status.HTTP_200_OK) def get_tokens_for_user(user): refresh = RefreshToken.for_user(user) @@ -246,9 +304,69 @@ class RoleViewSet(ModelViewSet): serializer_class = RoleSerializer pagination_class = None search_fields = ['name'] + filterset_fields = [] ordering_fields = ['id'] ordering = 'id' +class UserExamViewset(ImpMixin, ModelViewSet): + """ + 用户考试:增删改查 + """ + perms_map = {'get': '*', 'post': 'user_exam_create', + 'put': 'user_exam_create', 'delete': 'user_exam_delete'} + queryset = User.objects.all().order_by('-id') + serializer_class = UserListSerializer + filterset_class = UserFilter + search_fields = ['username', 'name', 'phone', 'email'] + ordering_fields = ['-id'] + + def get_queryset(self): + queryset = self.queryset + dept = self.request.user.dept.id + deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept)) + queryset = queryset.filter(dept__in=deptqueryset) + return queryset + + def create(self, request, *args, **kwargs): + # 创建用户默认添加密码 + password = request.data['password'] if 'password' in request.data else None + if password: + password = make_password(password) + else: + # password = make_password(''.join(random.sample(string.ascii_letters + string.digits, 8))) + password = make_password('0000') + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save(password=password) + return Response(serializer.data) + + def build_data(self, sheet, start): + data_list = [] + exam_role = Role.objects.get(name='考试') + if not exam_role: + return Response({'msg': '考试角色不存在'}) + for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据 + if row[0] is not None: + dept = Organization.objects.get(name=row[3]) + if not dept: + return Response({'msg': '部门不存在'}) + serializer_data = { + 'name': row[1], + 'username':row[2], + 'dept':dept.id, + 'roles':[exam_role.id], + 'avatar': "/media/default/avatar.png" + } + data_list.append(serializer_data) + return data_list + @action(detail=False, methods=['post']) + @transaction.atomic + def imp(self, request, *args, **kwargs): + """ + 导入数据 + """ + return self.gen_imp_view(request, 2, UserListSerializer) + class UserViewSet(PageOrNot, ModelViewSet): """ @@ -532,5 +650,6 @@ class InitAreaView(APIView): for i in cs: City.objects.create(id=i['code'], name=i['name'], parent=Province.objects.get(id=i['provinceCode'])) return Response() +