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 @@
+
+
+
+
+
+
+ 部门
+
+
+
+
+
+
+
+
+ 用户
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+ 新增
+
+
+
+
+ {{ scope.row.name }}
+
+
+ {{ scope.row.username }}
+
+
+ {{
+ scope.row.dept_name
+ }}
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+ {{ scope.row.date_joined }}
+
+
+
+
+ 重置密码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确认
+
+
+
+
+
+
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()
+