This commit is contained in:
caoqianming 2020-04-26 17:44:10 +08:00
parent 875069ac9f
commit 426c2fdae1
13 changed files with 200 additions and 33 deletions

View File

@ -6,3 +6,4 @@ export function getBasicCount() {
method: 'get', method: 'get',
}) })
} }

View File

@ -61,4 +61,11 @@ export function importConsumer(data) {
method: 'post', method: 'post',
data data
}) })
}
export function exportConsumer(query) {
return request({
url: '/crm/consumer/export',
method: 'get',
params: query
})
} }

View File

@ -117,4 +117,11 @@ export function deletePaper(id) {
url: `/examtest/paper/${id}/`, url: `/examtest/paper/${id}/`,
method: 'delete', method: 'delete',
}) })
}
export function exportTest(query) {
return request({
url: '/examtest/examtest/export',
method: 'get',
params: query
})
} }

View File

@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth'
const service = axios.create({ const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests // withCredentials: true, // send cookies when cross-domain requests
timeout: 10000, // request timeout timeout: 30000, // request timeout
}) })
// request interceptor // request interceptor

View File

@ -31,12 +31,23 @@
:value="item.value" :value="item.value"
/> />
</el-select> </el-select>
<el-date-picker
v-model="value"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
<el-button <el-button
class="filter-item" class="filter-item"
type="primary" type="primary"
icon="el-icon-refresh-left" icon="el-icon-refresh-left"
@click="resetFilter" @click="resetFilter"
>刷新重置</el-button> >刷新重置</el-button>
<el-button type="primary" icon="el-icon-download" @click="exportTest">导出Excel</el-button>
<div style="margin-top:10px"> <div style="margin-top:10px">
</div> </div>
</div> </div>
@ -67,7 +78,7 @@
<template slot-scope="scope">{{ scope.row.score }}</template> <template slot-scope="scope">{{ scope.row.score }}</template>
</el-table-column> </el-table-column>
<el-table-column align="left" label="耗时(秒)"> <el-table-column align="left" label="耗时(秒)">
<template slot-scope="scope">{{ scope.row.took }}</template> <template slot-scope="scope">{{ scope.row.took_format }}</template>
</el-table-column> </el-table-column>
<el-table-column align="left" label="答题时间"> <el-table-column align="left" label="答题时间">
<template slot-scope="scope">{{ scope.row.start_time }}</template> <template slot-scope="scope">{{ scope.row.start_time }}</template>
@ -94,7 +105,7 @@
</template> </template>
<script> <script>
import { getExamTestlist } from "@/api/examtest"; import { getExamTestlist, exportTest } from "@/api/examtest";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
@ -121,6 +132,42 @@ export default {
{ key: true, label: "通过", value: true }, { key: true, label: "通过", value: true },
{ key: false, label: "未通过", value: false}, { key: false, label: "未通过", value: false},
], ],
pickerOptions: {
shortcuts: [{
text: '最近一天',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
value: '',
}; };
}, },
computed: {}, computed: {},
@ -152,6 +199,13 @@ export default {
// this.dialogVisible = true // this.dialogVisible = true
// this.question = scope.row // this.question = scope.row
}, },
exportTest() {
const loading = this.$loading();
exportTest(this.listQuery).then(response => {
loading.close()
window.open(response.data.path, "_blank");
});
}
} }
}; };
</script> </script>

View File

@ -32,7 +32,7 @@
/> />
</el-select> --> </el-select> -->
<el-input <el-input
v-model="search" v-model="listQuery.search"
placeholder="姓名或手机号" placeholder="姓名或手机号"
style="width: 200px;" style="width: 200px;"
class="filter-item" class="filter-item"
@ -68,8 +68,9 @@
<el-button size="small" type="primary" @click="popovervisible = false">上传导入</el-button> <el-button size="small" type="primary" @click="popovervisible = false">上传导入</el-button>
</el-upload> </el-upload>
</div> </div>
<el-button slot="reference">Excel导入</el-button> <el-button slot="reference" icon="el-icon-upload2">Excel导入</el-button>
</el-popover> </el-popover>
<el-button type="primary" icon="el-icon-download" @click="exportConsumer">导出Excel</el-button>
</div> </div>
<el-table <el-table
:data="consumerList" :data="consumerList"
@ -210,7 +211,8 @@ import {
createConsumer, createConsumer,
deleteConsumer, deleteConsumer,
updateConsumer, updateConsumer,
importConsumer importConsumer,
exportConsumer
} from "@/api/crm"; } from "@/api/crm";
import { getSubjectAll } from "@/api/question" import { getSubjectAll } from "@/api/question"
import { getWorkScopeAll } from "@/api/examtest" import { getWorkScopeAll } from "@/api/examtest"
@ -231,7 +233,8 @@ const defaultConsumer = {
}; };
const listQuery = { const listQuery = {
page: 1, page: 1,
limit: 20 limit: 20,
search:''
}; };
export default { export default {
components: { Pagination }, components: { Pagination },
@ -251,9 +254,9 @@ export default {
listLoading: true, listLoading: true,
listQuery: { listQuery: {
page: 1, page: 1,
limit: 20 limit: 20,
search:''
}, },
search: "",
enabledOptions: [ enabledOptions: [
{ key: "true", display_name: "已缴" }, { key: "true", display_name: "已缴" },
{ key: "false", display_name: "未缴" } { key: "false", display_name: "未缴" }
@ -341,7 +344,8 @@ export default {
resetFilter() { resetFilter() {
this.listQuery = { this.listQuery = {
page: 1, page: 1,
limit: 20 limit: 20,
search: ''
}; };
this.getList(); this.getList();
}, },
@ -350,7 +354,7 @@ export default {
this.getList(); this.getList();
}, },
handleSearch() { handleSearch() {
this.getList({ search: this.search }); this.getList(this.listQuery);
}, },
handleAddUser() { handleAddUser() {
this.consumer = Object.assign({}, defaultConsumer); this.consumer = Object.assign({}, defaultConsumer);
@ -430,6 +434,13 @@ export default {
return false; return false;
} }
}); });
},
exportConsumer() {
const loading = this.$loading();
exportConsumer(this.listQuery).then(response => {
loading.close()
window.open(response.data.path, "_blank");
});
} }
} }
}; };

View File

@ -0,0 +1,26 @@
from openpyxl.workbook import Workbook
from django.conf import settings
from datetime import datetime
from openpyxl.styles import Font, Fill
BASE_DIR = settings.BASE_DIR
def export_consumer(users):
'''
params: serializer users
return: xlsx path
'''
wb = Workbook()
ws1 = wb.active
ws1.title = '用户表'
ws1.append(['姓名','手机号', '单位', '微信昵称', '工作类别', '创建日期'])
row = ws1.row_dimensions[1]
row.font = Font(bold=True)
for i in users:
ws1.append([i['name'], i['username'], i['company_name'], i['nickname'], i['workscope_name'], i['create_time']])
filename = 'users' + datetime.now().strftime("%Y%m%d%H%M%S") +'.xlsx'
path = '/media/export/' + filename
wb.save((BASE_DIR + path).replace('\\', '/'))
return path

View File

@ -5,6 +5,7 @@ from calendar import timegm
from datetime import datetime from datetime import datetime
import requests import requests
from django.db.models import Q
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from openpyxl import Workbook, load_workbook from openpyxl import Workbook, load_workbook
from rest_framework import status from rest_framework import status
@ -15,7 +16,8 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler from rest_framework_jwt.serializers import (jwt_encode_handler,
jwt_payload_handler)
from rest_framework_jwt.settings import api_settings from rest_framework_jwt.settings import api_settings
from crm.zhenzismsclient import ZhenziSmsClient from crm.zhenzismsclient import ZhenziSmsClient
@ -25,6 +27,7 @@ from question.serializers import QuestionSerializer
from server import settings from server import settings
from utils.custom import CommonPagination from utils.custom import CommonPagination
from .exports import export_consumer
from .models import Company, Consumer, PaySubject, SendCode from .models import Company, Consumer, PaySubject, SendCode
from .serializers import CompanySerializer, ConsumerSerializer from .serializers import CompanySerializer, ConsumerSerializer
@ -229,10 +232,13 @@ class ConsumerViewSet(ModelViewSet):
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
@action(methods=['get'], detail=False, @action(methods=['get'], detail=False,
url_path='export', url_name='export_consumer') url_path='export', url_name='export_consumer', perms_map=[{'*':'export_consumer'}])
def export(self, request): def export(self, request):
queryset = self.filter_queryset(self.queryset) queryset = self.filter_queryset(self.queryset)
print(queryset) queryset = ConsumerSerializer.setup_eager_loading(queryset) # 性能优化
serializer = ConsumerSerializer(instance=queryset, many=True)
path = export_consumer(serializer.data)
return Response({'path': path})
class ConsumerMPLoginView(APIView): class ConsumerMPLoginView(APIView):
""" """

View File

@ -0,0 +1,26 @@
from openpyxl.workbook import Workbook
from django.conf import settings
from datetime import datetime
from openpyxl.styles import Font, Fill
BASE_DIR = settings.BASE_DIR
def export_test(tests):
'''
params: serializer tests
return: xlsx path
'''
wb = Workbook()
ws1 = wb.active
ws1.title = '用户表'
ws1.append(['类型','用户姓名', '用户单位', '工作类别', '押题卷', '得分', '耗时', '答题时间'])
row = ws1.row_dimensions[1]
row.font = Font(bold=True)
for i in tests:
ws1.append([i['type'], i['consumer_name'], i['consumer_company_name'], i['workscope_name'], i['paper_name'], i['score'], i['took_format'], i['start_time']])
filename = 'tests' + datetime.now().strftime("%Y%m%d%H%M%S") +'.xlsx'
path = '/media/export/' + filename
wb.save((BASE_DIR + path).replace('\\', '/'))
return path

View File

@ -62,6 +62,8 @@ class ExamTestListSerializer(serializers.ModelSerializer):
workscope_name = serializers.StringRelatedField(source='workscope', read_only=True) workscope_name = serializers.StringRelatedField(source='workscope', read_only=True)
paper_name = serializers.StringRelatedField(source='paper', read_only=True) paper_name = serializers.StringRelatedField(source='paper', read_only=True)
consumer_name = serializers.SerializerMethodField() consumer_name = serializers.SerializerMethodField()
took_format = serializers.SerializerMethodField()
consumer_company_name = serializers.SerializerMethodField()
class Meta: class Meta:
model = ExamTest model = ExamTest
exclude = ('detail',) exclude = ('detail',)
@ -69,6 +71,21 @@ class ExamTestListSerializer(serializers.ModelSerializer):
def get_consumer_name(self, obj): def get_consumer_name(self, obj):
return obj.consumer.name return obj.consumer.name
def get_took_format(self, obj):
m, s = divmod(obj.took, 60)
h, m = divmod(m, 60)
return "%02d:%02d:%02d" % (h, m, s)
def get_consumer_company_name(self, obj):
if obj.consumer.company:
return obj.consumer.company.name
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('consumer','paper', 'workscope')
return queryset
class AnswerDetailSerializer(serializers.ModelSerializer): class AnswerDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = AnswerDetail model = AnswerDetail

View File

@ -18,6 +18,7 @@ from question.serializers import QuestionSerializer
from server import settings from server import settings
from utils.custom import CommonPagination from utils.custom import CommonPagination
from .exports import export_test
from .models import AnswerDetail, Banner, ExamTest from .models import AnswerDetail, Banner, ExamTest
from .models_paper import Paper, PaperQuestions, TestRule, WorkScope from .models_paper import Paper, PaperQuestions, TestRule, WorkScope
from .serializers import ( from .serializers import (
@ -258,8 +259,15 @@ class ExamTestViewSet(ModelViewSet):
return Response({'error':'答题记录不存在'}) return Response({'error':'答题记录不存在'})
else: else:
return Response(serializer.errors) return Response(serializer.errors)
@action(methods=['get'], detail=False,
url_path='export', url_name='export_test', perms_map=[{'*':'export_test'}])
def export(self, request):
queryset = self.filter_queryset(self.queryset)
queryset = ExamTestListSerializer.setup_eager_loading(queryset) # 性能优化
serializer = ExamTestListSerializer(instance=queryset, many=True)
path = export_test(serializer.data)
return Response({'path': path})
class PaperViewSet(ModelViewSet): class PaperViewSet(ModelViewSet):
""" """
押题卷增删改查 押题卷增删改查

View File

@ -1,22 +1,26 @@
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework import status
from django_filters.rest_framework import DjangoFilterBackend
from openpyxl import Workbook, load_workbook
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
import json import json
from utils.custom import CommonPagination from django_filters.rest_framework import DjangoFilterBackend
from .models import Questioncat, Question from openpyxl import Workbook, load_workbook
from .serializers import QuestioncatSerializer, QuestionSerializer, SubjectSerializer, QuestioncatSerializerDefault from rest_framework import status
from server import settings from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from crm.models import PaySubject from crm.models import PaySubject
from examtest.models import WorkScope from examtest.models import WorkScope
from server import settings
from utils.custom import CommonPagination
from .models import Question, Questioncat
from .serializers import (QuestioncatSerializer, QuestioncatSerializerDefault,
QuestionSerializer, SubjectSerializer)
class SubjectViewSet(ModelViewSet): class SubjectViewSet(ModelViewSet):
@ -240,4 +244,4 @@ class ExerciseView(APIView):
i['is_collect'] = True i['is_collect'] = True
else: else:
i['is_collect'] = False i['is_collect'] = False
return Response({'count':count, 'results':results}) return Response({'count':count, 'results':results})

View File

@ -15,7 +15,7 @@ class UploadFileView(APIView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
fileobj = request.FILES['file'] fileobj = request.FILES['file']
file_name = fileobj.name.encode('utf-8').decode('utf-8') file_name = fileobj.name.encode('utf-8').decode('utf-8')
file_name_new = str(uuid.uuid1()) + '.' + file_name.split('.')[1] file_name_new = str(uuid.uuid1()) + '.' + file_name.split('.')[-1]
subfolder = os.path.join('media', datetime.now().strftime("%Y%m%d")) subfolder = os.path.join('media', datetime.now().strftime("%Y%m%d"))
if not os.path.exists(subfolder): if not os.path.exists(subfolder):
os.mkdir(subfolder) os.mkdir(subfolder)