monitest complete

This commit is contained in:
caoqianming 2020-03-20 18:24:17 +08:00
parent 54ee7e06f1
commit fc66951a17
46 changed files with 1053 additions and 153 deletions

View File

@ -16,7 +16,7 @@
:value="item.value" :value="item.value"
/> />
</el-select> </el-select>
<el-select <!-- <el-select
v-model="listQuery.is_paid" v-model="listQuery.is_paid"
placeholder="是否缴费" placeholder="是否缴费"
clearable clearable
@ -30,7 +30,7 @@
:label="item.display_name" :label="item.display_name"
:value="item.key" :value="item.key"
/> />
</el-select> </el-select> -->
<el-input <el-input
v-model="search" v-model="search"
placeholder="姓名" placeholder="姓名"
@ -96,10 +96,14 @@
v-if="scope.row.company_name != null" v-if="scope.row.company_name != null"
>{{ scope.row.company_name }}</template> >{{ scope.row.company_name }}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="是否付费"> <el-table-column align="center" label="缴费科目">
<template slot-scope="scope"> <template slot-scope="scope" >
<el-tag type="success" v-if="scope.row.is_paid">已付费</el-tag> <el-tag
<el-tag type="danger" v-else>未付费</el-tag> v-for="item in scope.row.subjects"
:key="item.id"
effect="dark">
{{ item.name }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建日期"> <el-table-column label="创建日期">
@ -153,7 +157,7 @@
</el-form-item> </el-form-item>
<el-form-item label="单位" prop="company"> <el-form-item label="单位" prop="company">
<el-cascader <el-cascader
v-model="consumer.company" v-model="consumer.company.id"
:options="companyData" :options="companyData"
:props="{ checkStrictly: true }" :props="{ checkStrictly: true }"
clearable clearable
@ -188,8 +192,10 @@ const defaultConsumer = {
id: "", id: "",
name: "", name: "",
username: "", username: "",
company: null, company: {
is_paid: false id:0,
name:''
},
}; };
const listQuery = { const listQuery = {
page: 1, page: 1,
@ -206,13 +212,7 @@ export default {
return { return {
uploadUrl: uploadUrl(), uploadUrl: uploadUrl(),
popovervisible: false, popovervisible: false,
consumer: { consumer: defaultConsumer,
id: "",
name: "",
username: "",
company: null,
ispaid: false
},
myHeaders: { Authorization: "JWT " + getToken() }, myHeaders: { Authorization: "JWT " + getToken() },
consumerList: [], consumerList: [],
total: 0, total: 0,
@ -345,7 +345,9 @@ export default {
if (valid) { if (valid) {
const isEdit = this.dialogType === "edit"; const isEdit = this.dialogType === "edit";
if (isEdit) { if (isEdit) {
updateConsumer(this.consumer.id, this.consumer).then(() => { let consumer = this.consumer
consumer.company = consumer.company.id
updateConsumer(this.consumer.id, consumer).then(() => {
for (let index = 0; index < this.consumerList.length; index++) { for (let index = 0; index < this.consumerList.length; index++) {
if (this.consumerList[index].id === this.consumer.id) { if (this.consumerList[index].id === this.consumer.id) {
this.consumerList.splice( this.consumerList.splice(
@ -365,8 +367,10 @@ export default {
}); });
}); });
} else { } else {
this.consumer.company = this.consumer.company.pop(); this.consumer.company.id = this.consumer.company.id.pop();
createConsumer(this.consumer).then(res => { let consumer = this.consumer
consumer.company = consumer.company.id
createConsumer(consumer).then(res => {
// this.consumer = res.data // this.consumer = res.data
// this.consumerList.unshift(this.consumer) // this.consumerList.unshift(this.consumer)
this.getList(); this.getList();

View File

@ -11,7 +11,11 @@
"pages/cuoti/index", "pages/cuoti/index",
"pages/moni/index", "pages/moni/index",
"pages/moni/note", "pages/moni/note",
"pages/test/test" "pages/test/test",
"pages/test/result",
"pages/test/detail",
"pages/test/sheet",
"pages/test/list"
], ],
"window": { "window": {
"backgroundTextStyle": "light", "backgroundTextStyle": "light",

View File

@ -8,20 +8,20 @@ Page({
*/ */
data: { data: {
ctms: [], ctms: [],
tmIndex: 0, tm_index: 0,
isright:false, isright:false,
answerP:false answerP:false
}, },
radioChange: function (e) { radioChange: function (e) {
var that = this var that = this
that.data.currentTm['user_answer'] = e.detail.value that.data.currentTm['user_answer'] = e.detail.value
that.data.ctms[that.data.tmIndex] = that.data.currentTm that.data.ctms[that.data.tm_index] = that.data.currentTm
that.showAnswer() that.showAnswer()
}, },
checkboxChange: function (e) { checkboxChange: function (e) {
var that = this var that = this
that.data.currentTm['user_answer'] = e.detail.value that.data.currentTm['user_answer'] = e.detail.value
that.data.ctms[that.data.tmIndex] = that.data.currentTm that.data.ctms[that.data.tm_index] = that.data.currentTm
}, },
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
@ -64,7 +64,7 @@ Page({
} }
}) })
}else{ }else{
that.showTm(that.data.tmIndex) that.showTm(that.data.tm_index)
} }
}, },
@ -123,7 +123,7 @@ Page({
var that = this var that = this
var currentTm = that.data.ctms[index] var currentTm = that.data.ctms[index]
that.setData({ that.setData({
'tmIndex': index, 'tm_index': index,
'tmtotal':that.data.ctms.length, 'tmtotal':that.data.ctms.length,
'currentTm': currentTm 'currentTm': currentTm
}) })
@ -165,13 +165,13 @@ Page({
}, },
next: function () { next: function () {
var that = this var that = this
var tmIndex = that.data.tmIndex + 1 var tm_index = that.data.tm_index + 1
that.showTm(tmIndex) that.showTm(tm_index)
}, },
previous: function () { previous: function () {
var that = this var that = this
var tmIndex = that.data.tmIndex - 1 var tm_index = that.data.tm_index - 1
that.showTm(tmIndex) that.showTm(tm_index)
}, },
showOptions: function () { showOptions: function () {
let currentTm = this.data.currentTm let currentTm = this.data.currentTm
@ -195,7 +195,7 @@ Page({
}, },
remove: function () { remove: function () {
var that = this var that = this
var index = that.data.tmIndex var index = that.data.tm_index
that.data.ctms.splice(index,1) that.data.ctms.splice(index,1)
if(that.data.ctms.length>index){ if(that.data.ctms.length>index){
that.showTm(index) that.showTm(index)

View File

@ -1,9 +1,9 @@
<view class="head"> <view class="head">
题量: {{tmIndex+1}}/{{tmtotal}} 题量: {{tm_index+1}}/{{tmtotal}}
</view> </view>
<scroll-view scroll-y="true" style="height: {{scrollHeight}}px;"> <scroll-view scroll-y="true" style="height: {{scrollHeight}}px;">
<view class="weui-article"> <view class="weui-article">
<view class="weui-article__h2">{{tmIndex+1}}. <view class="weui-article__h2">{{tm_index+1}}.
<span class="txlabel">{{currentTm.type}}</span> <span class="txlabel">{{currentTm.type}}</span>
</view> </view>
<view style="text-align:center"> <view style="text-align:center">
@ -52,10 +52,10 @@
</scroll-view> </scroll-view>
<view class="btns"> <view class="btns">
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="previous" disabled="{{tmIndex==0}}">上题</button> <button type="primary" size="mini" bindtap="previous" disabled="{{tm_index==0}}">上题</button>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="next" disabled="{{tmIndex==tmtotal-1}}">下题</button> <button type="primary" size="mini" bindtap="next" disabled="{{tm_index==tmtotal-1}}">下题</button>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="showAnswer">答案</button> <button type="primary" size="mini" bindtap="showAnswer">答案</button>

View File

@ -11,14 +11,14 @@ Page({
tms:[], tms:[],
ydtms:[], ydtms:[],
ctms:[], ctms:[],
tmIndex: 0, tm_index: 0,
answerP: false, answerP: false,
isLoad:true, isLoad:true,
}, },
radioChange: function (e) { radioChange: function (e) {
var that = this var that = this
that.data.tm_current['user_answer'] = e.detail.value that.data.tm_current['user_answer'] = e.detail.value
that.data.tms[that.data.tmIndex] = that.data.tm_current that.data.tms[that.data.tm_index] = that.data.tm_current
that.showAnswer() that.showAnswer()
if (that.data.ydtms.indexOf(that.data.tm_current.id)==-1){ if (that.data.ydtms.indexOf(that.data.tm_current.id)==-1){
that.data.ydtms.push(that.data.tm_current.id) that.data.ydtms.push(that.data.tm_current.id)
@ -27,7 +27,7 @@ Page({
checkboxChange: function (e) { checkboxChange: function (e) {
var that = this var that = this
that.data.tm_current['user_answer'] = e.detail.value that.data.tm_current['user_answer'] = e.detail.value
that.data.tms[that.data.tmIndex] = that.data.tm_current that.data.tms[that.data.tm_index] = that.data.tm_current
if (that.data.ydtms.indexOf(that.data.tm_current.id) == -1) { if (that.data.ydtms.indexOf(that.data.tm_current.id) == -1) {
that.data.ydtms.push(that.data.tm_current.id) that.data.ydtms.push(that.data.tm_current.id)
} }
@ -81,7 +81,7 @@ Page({
}) })
}else{ }else{
that.data.tms = that.data.tms.concat(res.data.results) that.data.tms = that.data.tms.concat(res.data.results)
that.showTm(that.data.tmIndex) //展示题目和答案 that.showTm(that.data.tm_index) //展示题目和答案
if(that.data.isLoad){ if(that.data.isLoad){
that.setData({ that.setData({
tmtotal: res.data.total, tmtotal: res.data.total,
@ -152,7 +152,7 @@ Page({
var that = this var that = this
var tm_current = that.data.tms[index] var tm_current = that.data.tms[index]
that.setData({ that.setData({
'tmIndex': index, 'tm_index': index,
'tm_current': tm_current 'tm_current': tm_current
}) })
that.showOptions() that.showOptions()
@ -193,25 +193,25 @@ Page({
}, },
next: function () { next: function () {
var that = this var that = this
var tmIndex = that.data.tmIndex + 1 var tm_index = that.data.tm_index + 1
that.setData({ that.setData({
tmIndex: tmIndex, tm_index: tm_index,
answerP: false answerP: false
}) })
if (tmIndex + 1 > that.data.tms.length) { if (tm_index + 1 > that.data.tms.length) {
that.getTms() that.getTms()
} else { } else {
that.showTm(tmIndex) that.showTm(tm_index)
} }
}, },
previous: function () { previous: function () {
var that = this var that = this
var tmIndex = that.data.tmIndex - 1 var tm_index = that.data.tm_index - 1
that.setData({ that.setData({
answerP: false answerP: false
}) })
that.showTm(tmIndex) that.showTm(tm_index)
}, },
showOptions: function () { showOptions: function () {
let tm_current = this.data.tm_current let tm_current = this.data.tm_current

View File

@ -1,9 +1,9 @@
<view class="head"> <view class="head">
题量: {{tmIndex+1}}/{{tmtotal}} 题量: {{tm_index+1}}/{{tmtotal}}
</view> </view>
<scroll-view scroll-y="true" style="height: {{scrollHeight}}px;"> <scroll-view scroll-y="true" style="height: {{scrollHeight}}px;">
<view class="weui-article"> <view class="weui-article">
<view class="weui-article__h2">{{tmIndex+1}}. <view class="weui-article__h2">{{tm_index+1}}.
<span class="txlabel">{{tm_current.type}}</span> <span class="txlabel">{{tm_current.type}}</span>
</view> </view>
<view class="weui-article__title">{{tm_current.name}}</view> <view class="weui-article__title">{{tm_current.name}}</view>
@ -48,10 +48,10 @@
</scroll-view> </scroll-view>
<view class="btns"> <view class="btns">
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="previous" disabled="{{tmIndex==0}}">上题</button> <button type="primary" size="mini" bindtap="previous" disabled="{{tm_index==0}}">上题</button>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="next" disabled="{{tmIndex==tmtotal-1}}">下题</button> <button type="primary" size="mini" bindtap="next" disabled="{{tm_index==tmtotal-1}}">下题</button>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="showAnswer">答案</button> <button type="primary" size="mini" bindtap="showAnswer">答案</button>

View File

@ -143,5 +143,16 @@ Page({
duration: 1500 duration: 1500
}) })
} }
},
goYati: function () {
wx.showToast({
title: '暂未开放',
icon:'none'
})
},
testList: function (){
wx.navigateTo({
url: '/pages/test/list',
})
} }
}) })

View File

@ -47,14 +47,14 @@
</view> </view>
<view class="weui-grid__label">自助模考</view> <view class="weui-grid__label">自助模考</view>
</a> </a>
<a class="weui-grid"> <a class="weui-grid" bindtap="goYati">
<view class="weui-grid__icon"> <view class="weui-grid__icon" >
<image src="/images/yati.svg" alt></image> <image src="/images/yati.svg" alt></image>
</view> </view>
<view class="weui-grid__label">押题考试</view> <view class="weui-grid__label">押题考试</view>
</a> </a>
<a class="weui-grid"> <a class="weui-grid" bindtap="testList">
<view class="weui-grid__icon"> <view class="weui-grid__icon" >
<image src="/images/ksjl.svg" alt></image> <image src="/images/ksjl.svg" alt></image>
</view> </view>
<view class="weui-grid__label">考试记录</view> <view class="weui-grid__label">考试记录</view>

View File

@ -91,7 +91,7 @@ Page({
wx.showLoading({ wx.showLoading({
title: '正在生成试卷', title: '正在生成试卷',
}) })
api.request('examtest/monitest/', 'GET', { 'rule': e.currentTarget.id }).then(res => { api.request('examtest/testrule/' + e.currentTarget.id + '/monitest', 'GET').then(res => {
try { try {
wx.setStorageSync('monitest', res.data) wx.setStorageSync('monitest', res.data)
} catch (e) { } } catch (e) { }

View File

@ -0,0 +1,125 @@
// pages/test/detail.js
const api = require("../../utils/request.js");
Page({
/**
* 页面的初始数据
*/
data: {
tm_index:0,
results:[]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this
var query = {'examtest':options.id}
api.request('examtest/answerdetail/', 'GET', query).then(res => {
that.data.results= res.data
that.showTm(0)
that.setData({
tmtotal:res.data.length
})
})
try {
const res = wx.getSystemInfoSync()
that.setData({
scrollHeight: res.windowHeight - 70
})
} catch (e) {
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
showTm: function (index) {
var that = this
var tm_current = that.data.results[index]
that.setData({
'tm_index': index,
'tm_current': tm_current
})
that.showOptions()
},
next: function () {
var that = this
var tm_index = that.data.tm_index + 1
that.showTm(tm_index)
},
previous: function () {
var that = this
var tm_index = that.data.tm_index - 1
that.showTm(tm_index)
},
showOptions: function () {
var that = this
let question_options = that.data.tm_current.question.options
let options = []
let user_answer = that.data.tm_current.user_answer
for (let key in question_options) {
let option = {}
option.key = key
option.value = key + ':' + question_options[key]
if (user_answer) {
if (key == user_answer || user_answer.indexOf(key) != -1) {
option.checked = true
}
} else {
option.checked = false
}
options.push(option)
}
that.setData({
options: options
})
},
})

View File

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@ -0,0 +1,60 @@
<view class="head">
题量: {{tm_index+1}}/{{tmtotal}}
</view>
<scroll-view scroll-y="true" style="height: {{scrollHeight}}px;">
<view class="weui-article">
<view class="weui-article__h2">{{tm_index+1}}.
<span class="txlabel">{{tm_current.question.type}}</span>
</view>
<view class="weui-article__title">{{tm_current.question.name}}</view>
</view>
<view class="weui-cells weui-cells_radio">
<radio-group class="radio-group" wx:if="{{(tm_current.question.type=='单选' ||tm_current.question.type=='判断')}}">
<label class="weui-cell weui-cell_active weui-check__label" wx:for="{{options}}" wx:key="key">
<view class="weui-cell__bd">
<view>{{item.value}}</view>
</view>
<view class="weui-cell__ft">
<radio class="weui-check" value="{{item.key}}" checked="{{item.checked}}" />
<i class="weui-icon-checked"></i>
</view>
</label>
</radio-group>
</view>
<view class="weui-cells weui-cells_checkbox">
<checkbox-group bindchange="checkboxChange" wx:if="{{tm_current.question.type=='多选'}}">
<label class="weui-cell weui-cell_active weui-check__label" wx:for="{{options}}" wx:key="key">
<view class="weui-cell__bd">
<view>{{item.value}}</view>
</view>
<view class="weui-cell__hd">
<checkbox class="weui-check" value="{{item.key}}" checked="{{item.checked}}"/>
<i class="weui-icon-checked"></i>
</view>
</label>
</checkbox-group>
</view>
<view class="weui-article">
<view class="weui-article__h2" wx:if="{{tm_current.is_right}}" style="color:green;font-weight:bold">回答正确!</view>
<view class="weui-article__h2" wx:else style="color:red;font-weight:bold">回答有误!</view>
<view class="weui-article__h2">正确答案是{{tm_current.question.right}},你的答案是{{tm_current.user_answer}}</view>
<view class="weui-article__title">
<span style="color:blue">解析: </span>
<span wx:if="{{tm_current.question.resoluation != null}}">{{tm_current.question.resoluation}}</span>
<span wx:else>无</span>
</view>
</view>
</scroll-view>
<view class="btns">
<view style="width:33%">
<button type="primary" size="mini" bindtap="previous" disabled="{{tm_index==0}}">上题</button>
</view>
<view style="width:33%">
<button type="primary" size="mini" bindtap="next" disabled="{{tm_index==tmtotal-1}}">下题</button>
</view>
<view style="width:33%">
<image style="height: 80%;" src="/images/weishoucang.svg" bindtap="shoucang" wx:if="weishoucang"></image>
<image style="height: 80%;" src="/images/yishoucang.svg" bindtap="shoucang" wx:else></image>
</view>
</view>

View File

@ -0,0 +1,20 @@
.head{
width:100%;
height:30px;
color:#fff;
background-color: cornflowerblue;
text-align: center;
}
.btns{
height:40px;
display:flex;
flex-direction: row;
flex-wrap: wrap;
text-align:center;
}
.txlabel{
color:#fff;
background-color: rgb(216, 140, 0);
padding: 2px
}

View File

@ -0,0 +1,102 @@
// pages/lianxi/index.js
const api = require("../../utils/request.js");
Page({
/**
* 页面的初始数据
*/
data: {
results: [],
query:{
page:1,
limit:10
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function () {
var that = this
that.getList(that.data.query)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
getList: function () {
var that = this
api.request('examtest/myexamtest/', 'GET', that.data.query).then(res => {
if(res.data.length==0){
wx.showToast({
title: '没有更多了',
icon:''
})
}else{
if (that.data.query.page == 1){
that.data.results = res.data.results
}else{
that.data.results = that.data.results.concat(res.data.results)
}
that.setData({
results:that.data.results,
count:res.data.count
})
}
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
var that = this
that.data.query.page = 1;
that.getList();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
that.data.query.page = that.data.query.page + 1
that.getList()
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
detail: function (e) {
wx.navigateTo({
url: '',
})
},
})

View File

@ -0,0 +1,6 @@
{
"usingComponents": {},
"navigationBarTitleText": "考试记录",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50
}

View File

@ -0,0 +1,30 @@
<view class="head">
共{{count}}条考试记录</view>
<view class="weui-cells weui-cells_after-title">
<block wx:for="{{results}}" wx:key="unique">
<navigator url="detail?id={{item.id}}" class="weui-media-box weui-media-box_appmsg" hover-class="weui-cell_active">
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
<view class="weui-media-box__title">{{item.name}}</view>
<view class="weui-media-box__desc" style="color:orange">
<span style="font-weight:bold;color:white;background-color:orange;">{{item.type}}</span>
<span>-</span>
<span style="font-weight:bold;color:white;background-color:green;" wx:if="{{item.is_pass}}">通过</span>
<span style="font-weight:bold;color:white;background-color:red;" wx:else>未通过</span>
(得分
<span style="color:green;font-weight:bold" wx:if="{{item.is_pass}}">{{item.score}}</span>
<span style="color:red;font-weight:bold" wx:else>{{item.score}}</span>
-满分
<span style="font-weight:bold">{{item.total_score}})</span>
</view>
<view class="weui-media-box__desc" style="color:orange">
耗时:{{item.took}}s
开始答题:{{item.start_time}}
</view>
</view>
<view class="weui-panel__ft weui-cell__ft_in-access">
<view class="weui-media-box__desc">答卷详情</view>
</view>
</navigator>
</block>
</view>

View File

@ -0,0 +1,5 @@
.head{
color:#fff;
background-color: cornflowerblue;
text-align: center;
}

View File

@ -0,0 +1,76 @@
// pages/examtest/result.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function () {
this.setData(getApp().globalData.testData)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
detail: function () {
wx.redirectTo({
url: 'detail?id=' + this.data.id,
})
},
back: function () {
wx.switchTab({
url: '/pages/main/main'
})
},
})

View File

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@ -0,0 +1,29 @@
<view class="page">
<view class="weui-msg">
<view class="weui-msg__icon-area" wx:if="{{is_pass}}"><i class="weui-icon-success weui-icon_msg"></i></view>
<view class="weui-msg__icon-area" wx:else><i class="weui-icon-warn weui-icon_msg"></i></view>
<view class="weui-msg__text-area">
<h2 class="weui-msg__title" wx:if="{{is_pass}}">恭喜您,通过考试</h2>
<h2 class="weui-msg__title" wx:else>很遗憾,下次加油</h2>
<view class="weui-msg__desc">
得分
<span style="color:green;font-weight:bold" wx:if="{{is_pass}}">{{score}}</span>
<span style="color:red;font-weight:bold" wx:else>{{score}}</span>
——满分
<span style="font-weight:bold">{{total_score}}</span>
</view>
<view class="weui-msg__desc">
用时:
<span style="color:green;font-weight:bold" wx:if="{{is_pass}}">{{took}}</span>
<span style="color:red;font-weight:bold" wx:else>{{took}}</span>
</view>
</view>
<view class="weui-msg__opr-area">
<view class="weui-btn-area">
<a class="weui-btn weui-btn_primary" bindtap="detail">查看答卷详情</a>
<a class="weui-btn weui-btn_default" bindtap="back">返回首页</a>
</view>
</view>
</view>
</view>

View File

@ -0,0 +1 @@
/* pages/test/note.wxss */

View File

@ -0,0 +1,86 @@
// pages/examtest/sheet.js
Page({
/**
* 页面的初始数据
*/
data: {
},
back: function (e) {
console.log(e.currentTarget.dataset.index);
var tm_index = e.currentTarget.dataset.index
var pages = getCurrentPages();
var prevPage = pages[pages.length - 2]; //上一个页面
prevPage.setData({
tm_index: tm_index
})
prevPage.showTm(tm_index)
prevPage.showOptions()
wx.navigateBack({
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var pages = getCurrentPages();
var prevPage = pages[pages.length - 2]; //上一个页面
var tms = prevPage.data.tms //取上页data里的数据也可以修改
this.setData({
tms: tms
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

View File

@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationBarTitleText": "答题卡"
}

View File

@ -0,0 +1,13 @@
<!--pages/examtest/sheet.wxml-->
<view style="width:100%;display:flex;padding:20rpx;flex-wrap: wrap ">
<view wx:for="{{tms}}" wx:key="id" >
<view wx:if="{{item.user_answer && item.user_answer.length !=0 }}" class="ok" data-index="{{index}}" bindtap='back'>
{{index+1}}
</view>
<view wx:else class="no" data-index="{{index}}" bindtap='back'>
{{index+1}}
</view>
</view>
</view>

View File

@ -0,0 +1,21 @@
/* pages/examtest/sheet.wxss */
.ok{
display: flex;
color:#fff;
background-color: green;
border:4rpx solid green;
height:100rpx;
width:100rpx;
margin:20rpx;
justify-content: center;
align-items: center;
}
.no{
display: flex;
border:4rpx solid red;
height:100rpx;
width:100rpx;
margin:20rpx;
justify-content: center;
align-items: center;
}

View File

@ -43,6 +43,12 @@ Page({
}) })
} catch (e) { } catch (e) {
} }
try {
var value = wx.getStorageSync('ctms')
if (value) {
that.data.ctms = value
}
} catch (e) { }
let mil = that.data.monitest.limit * 60 * 1000 let mil = that.data.monitest.limit * 60 * 1000
let starttimes = (new Date()).getTime() //时间戳 let starttimes = (new Date()).getTime() //时间戳
let endtimes = starttimes + mil let endtimes = starttimes + mil
@ -78,6 +84,9 @@ Page({
*/ */
onUnload: function () { onUnload: function () {
clearInterval(getApp().globalData.timer) clearInterval(getApp().globalData.timer)
try {
wx.setStorageSync('ctms', this.data.ctms)
} catch (e) { }
}, },
/** /**
@ -178,6 +187,11 @@ Page({
options: options options: options
}) })
}, },
sheet: function () {
wx.navigateTo({
url: 'sheet',
})
},
hand: function () { hand: function () {
var that = this var that = this
for (var i = 0, len = that.data.tms.length; i < len; i++) { for (var i = 0, len = that.data.tms.length; i < len; i++) {
@ -219,19 +233,35 @@ Page({
for (var i = 0, len = that.data.tms.length; i < len; i++) { for (var i = 0, len = that.data.tms.length; i < len; i++) {
let tm_result = that.panTi(that.data.tms[i]) let tm_result = that.panTi(that.data.tms[i])
that.data.tms[i].is_right = tm_result.is_right that.data.tms[i].is_right = tm_result.is_right
if (tm_result.is_right == false){
that.data.ctms.unshift(that.data.tms[i])
if (that.data.ctms.length > 40) {
that.data.ctms.length = 40
}
}
that.data.tms[i].score = tm_result.score that.data.tms[i].score = tm_result.score
score = score + tm_result.score score = score + tm_result.score
} }
that.data.monitest.score = score that.data.monitest.score = score
if (score >= that.data.monitest.pass_score){
that.data.monitest.is_pass = true
}else{
that.data.monitest.is_pass = false
}
console.log(that.data.tms)
that.data.monitest.questions = that.data.tms that.data.monitest.questions = that.data.tms
that.data.monitest.start_time = util.formatTime(new Date(that.data.starttimes)) that.data.monitest.start_time = util.formatTime(new Date(that.data.starttimes))
that.data.monitest.end_time = util.formatTime(new Date()) that.data.monitest.end_time = util.formatTime(new Date())
that.data.monitest.took = Math.floor(((new Date()).getTime() - this.data.starttimes) / 1000) that.data.monitest.took = Math.floor(((new Date()).getTime() - this.data.starttimes) / 1000)
console.log(that.data.monitest) api.request('examtest/monitest/', 'POST', that.data.monitest).then(res => {
// api.request('examtest/monitest/', 'POST', that.data.monitest).then(res => { getApp().globalData.testData = res.data
// wx.redirectTo({ try {
// url: 'note', wx.removeStorageSync('monitest')
// }) } catch (e) {
// }) }
wx.redirectTo({
url: 'result',
})
})
} }
}) })

View File

@ -1,4 +1,4 @@
{ {
"usingComponents": {}, "usingComponents": {},
"navigationBarTitleText": "考试中" "navigationBarTitleText": "答题中"
} }

View File

@ -53,7 +53,7 @@
<button type="primary" size="mini" bindtap="next" disabled="{{tm_index==tm_total-1}}">下题</button> <button type="primary" size="mini" bindtap="next" disabled="{{tm_index==tm_total-1}}">下题</button>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<button type="primary" size="mini" bindtap="sheet">答题卡</button> <a class="weui-btn weui-btn_mini weui-btn_primary" bindtap="sheet">答题卡</a>
</view> </view>
<view style="width:25%"> <view style="width:25%">
<a class="weui-btn weui-btn_mini weui-btn_warn" bindtap="hand">交卷</a> <a class="weui-btn weui-btn_mini weui-btn_warn" bindtap="hand">交卷</a>

View File

@ -6,7 +6,7 @@ const formatTime = date => {
const minute = date.getMinutes() const minute = date.getMinutes()
const second = date.getSeconds() const second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
} }
const formatNumber = n => { const formatNumber = n => {

View File

@ -0,0 +1,39 @@
# Generated by Django 3.0.4 on 2020-03-20 09:26
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('question', '0007_auto_20200319_0846'),
('crm', '0006_auto_20200310_1531'),
]
operations = [
migrations.RemoveField(
model_name='consumer',
name='is_paid',
),
migrations.CreateModel(
name='PaySubject',
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_delete', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('consumer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crm.Consumer')),
('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='question.Questioncat')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='consumer',
name='subjects',
field=models.ManyToManyField(through='crm.PaySubject', to='question.Questioncat', verbose_name='付费学科'),
),
]

View File

@ -1,9 +1,10 @@
from django.db import models from django.db import models
import django.utils.timezone as timezone import django.utils.timezone as timezone
from rbac.models import CommonModel from rbac.models import SoftCommonModel, CommonModel
from question.models import Questioncat
# Create your models here. # Create your models here.
class Company(CommonModel): class Company(SoftCommonModel):
""" """
客户企业 客户企业
""" """
@ -17,7 +18,7 @@ class Company(CommonModel):
def __str__(self): def __str__(self):
return self.name return self.name
class Consumer(CommonModel): class Consumer(SoftCommonModel):
""" """
学员 学员
""" """
@ -27,7 +28,7 @@ class Consumer(CommonModel):
openid = models.CharField(max_length=200, verbose_name="openid", null=True, blank=True, unique=True) openid = models.CharField(max_length=200, verbose_name="openid", null=True, blank=True, unique=True)
avatar = models.CharField(default="/media/default/avatar.png",max_length=1000, null=True, blank=True, verbose_name="头像") avatar = models.CharField(default="/media/default/avatar.png",max_length=1000, null=True, blank=True, verbose_name="头像")
nickname = models.CharField(max_length=200, verbose_name="昵称", null=True, blank=True) nickname = models.CharField(max_length=200, verbose_name="昵称", null=True, blank=True)
is_paid = models.BooleanField(default=False,verbose_name="是否付费") subjects = models.ManyToManyField(Questioncat, verbose_name="付费学科", through="PaySubject")
class Meta: class Meta:
@ -35,4 +36,11 @@ class Consumer(CommonModel):
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
def __str__(self): def __str__(self):
return self.name return self.name
class PaySubject(CommonModel):
'''
付费学科关联表
'''
consumer = models.ForeignKey(Consumer, on_delete=models.CASCADE)
subject = models.ForeignKey(Questioncat, on_delete=models.CASCADE)

View File

@ -24,3 +24,15 @@ class ConsumerSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Consumer model = Consumer
fields = '__all__' fields = '__all__'
depth = 1
class ConsumerCUSerializer(serializers.ModelSerializer):
"""
客户新增编辑序列化
"""
create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
class Meta:
model = Consumer
fields = '__all__'

View File

@ -14,9 +14,10 @@ import json
from utils.custom import CommonPagination from utils.custom import CommonPagination
from rbac.permission import RbacPermission from rbac.permission import RbacPermission
from .models import Company, Consumer from .models import Company, Consumer, PaySubject
from .serializers import CompanySerializer, ConsumerSerializer from .serializers import CompanySerializer, ConsumerSerializer, ConsumerCUSerializer
from server import settings from server import settings
from question.models import Questioncat
appid = 'wx5c39b569f01c27db' appid = 'wx5c39b569f01c27db'
secret = '68762892f8df2b4a0b1940c5250a8dc0' secret = '68762892f8df2b4a0b1940c5250a8dc0'
@ -70,9 +71,14 @@ class ConsumerViewSet(ModelViewSet):
ordering_fields = ('id',) ordering_fields = ('id',)
ordering = ['id'] ordering = ['id']
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ('is_paid','company') filterset_fields = ('company',)
search_fields = ('^name',) search_fields = ('^name',)
def get_serializer_class(self):
if self.action == 'list':
return ConsumerSerializer
else:
return ConsumerCUSerializer
def destroy(self, request, *args, **kwargs): #逻辑删除 def destroy(self, request, *args, **kwargs): #逻辑删除
instance = self.get_object() instance = self.get_object()
@ -125,8 +131,10 @@ class ConsumerViewSet(ModelViewSet):
obj.name = name obj.name = name
obj.username = username obj.username = username
obj.company = companyobj obj.company = companyobj
obj.is_paid = True
obj.save() obj.save()
subjects = Questioncat.objects.filter(is_subject=True,is_delete=False)
if subjects.exists():
PaySubject.objects.create(subject=subjects.first(), consumer=obj)
i = i + 1 i = i + 1
if consumerdict: if consumerdict:
return {"code":206,"data":consumerdict,"msg":"导入部分成功"} return {"code":206,"data":consumerdict,"msg":"导入部分成功"}

View File

@ -50,7 +50,7 @@ class Migration(migrations.Migration):
field=models.FloatField(default=0, verbose_name='判断分数'), field=models.FloatField(default=0, verbose_name='判断分数'),
), ),
migrations.CreateModel( migrations.CreateModel(
name='MoniTest', name='ExamTest',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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='创建时间')), ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
@ -73,8 +73,8 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='answerdetail', model_name='answerdetail',
name='monitest', name='examtest',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='examtest.MoniTest'), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='examtest.ExamTest'),
), ),
migrations.AddField( migrations.AddField(
model_name='answerdetail', model_name='answerdetail',

View File

@ -29,7 +29,7 @@ class Migration(migrations.Migration):
options={'verbose_name': '答题记录', 'verbose_name_plural': '答题记录'}, options={'verbose_name': '答题记录', 'verbose_name_plural': '答题记录'},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='monitest', name='examtest',
options={'verbose_name': '自助模考', 'verbose_name_plural': '自助模考'}, options={'verbose_name': '自助模考', 'verbose_name_plural': '自助模考'},
), ),
] ]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-03-20 03:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('examtest', '0009_auto_20200319_1850'),
]
operations = [
migrations.AddField(
model_name='testrule',
name='is_pass',
field=models.BooleanField(default=True, verbose_name='是否通过'),
),
]

View File

@ -0,0 +1,42 @@
# Generated by Django 3.0.4 on 2020-03-20 03:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('examtest', '0010_testrule_is_pass'),
]
operations = [
migrations.RemoveField(
model_name='testrule',
name='is_pass',
),
migrations.AddField(
model_name='examtest',
name='is_pass',
field=models.BooleanField(default=True, verbose_name='是否通过'),
),
migrations.AddField(
model_name='examtest',
name='total_score',
field=models.FloatField(default=0, verbose_name='总分'),
),
migrations.AlterField(
model_name='examtest',
name='score',
field=models.FloatField(default=0, verbose_name='得分'),
),
migrations.AlterField(
model_name='testrule',
name='pass_score',
field=models.FloatField(default=0, verbose_name='及格分数'),
),
migrations.AlterField(
model_name='testrule',
name='total_score',
field=models.FloatField(default=0, verbose_name='满分'),
),
]

View File

@ -0,0 +1,50 @@
# Generated by Django 3.0.4 on 2020-03-20 06:39
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('examtest', '0011_auto_20200320_1115'),
]
operations = [
migrations.CreateModel(
name='Paper',
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_delete', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=200, verbose_name='名称')),
],
options={
'abstract': False,
},
),
migrations.DeleteModel(
name='PaperTest',
),
migrations.AlterModelOptions(
name='examtest',
options={'verbose_name': '考试表', 'verbose_name_plural': '考试表'},
),
migrations.AlterField(
model_name='examtest',
name='rule',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='examtest.TestRule', verbose_name='所用规则'),
),
migrations.AlterField(
model_name='examtest',
name='type',
field=models.CharField(choices=[('自助模考', '自助模考'), ('押卷模考', '押卷模考')], default='自助模考', max_length=50, verbose_name='考试类型'),
),
migrations.AddField(
model_name='examtest',
name='paper',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='examtest.Paper', verbose_name='所用试卷'),
),
]

View File

@ -1,11 +1,11 @@
from django.db import models from django.db import models
from rbac.models import CommonModel from rbac.models import SoftCommonModel, CommonModel
from django.contrib.postgres.fields import JSONField, ArrayField from django.contrib.postgres.fields import JSONField, ArrayField
from question.models import Questioncat, Question from question.models import Questioncat, Question
from crm.models import Consumer from crm.models import Consumer
# Create your models here. # Create your models here.
class TestRule(CommonModel): class TestRule(SoftCommonModel):
name = models.CharField(max_length=200, unique=True, verbose_name='名称') name = models.CharField(max_length=200, unique=True, verbose_name='名称')
desc = models.TextField(verbose_name='描述', default='') desc = models.TextField(verbose_name='描述', default='')
subject = models.ForeignKey(Questioncat, blank=True, null=True, on_delete=models.CASCADE, verbose_name='所属学科', related_name='subject') subject = models.ForeignKey(Questioncat, blank=True, null=True, on_delete=models.CASCADE, verbose_name='所属学科', related_name='subject')
@ -17,8 +17,9 @@ class TestRule(CommonModel):
panduan_count = models.IntegerField(default=0, verbose_name='判断数量') panduan_count = models.IntegerField(default=0, verbose_name='判断数量')
panduan_score = models.FloatField(default=0, verbose_name='判断分数') panduan_score = models.FloatField(default=0, verbose_name='判断分数')
limit = models.IntegerField(default=0, verbose_name='限时(分钟)') limit = models.IntegerField(default=0, verbose_name='限时(分钟)')
total_score = models.IntegerField(default=0, verbose_name='满分') total_score = models.FloatField(default=0, verbose_name='满分')
pass_score = models.IntegerField(default=0, verbose_name='及格分数') pass_score = models.FloatField(default=0, verbose_name='及格分数')
class Meta: class Meta:
verbose_name = '出题规则' verbose_name = '出题规则'
@ -27,31 +28,38 @@ class TestRule(CommonModel):
def __str__(self): def __str__(self):
return self.name return self.name
class PaperTest(CommonModel):
pass
class Meta:
verbose_name = '押题模考'
verbose_name_plural = verbose_name
class Paper(SoftCommonModel):
class MoniTest(CommonModel):
name = models.CharField(max_length=200, verbose_name='名称') name = models.CharField(max_length=200, verbose_name='名称')
type = models.CharField(max_length=50, default='自助模考',verbose_name='考试类型')
class ExamTest(CommonModel):
'''
硬删除
'''
type_choices = (
('自助模考', '自助模考'),
('押卷模考', '押卷模考'),
)
name = models.CharField(max_length=200, verbose_name='名称')
type = models.CharField(max_length=50, default='自助模考',choices = type_choices, verbose_name='考试类型')
limit = models.IntegerField(default=0, verbose_name='限时(分钟)') limit = models.IntegerField(default=0, verbose_name='限时(分钟)')
rule = models.ForeignKey(TestRule, on_delete=models.CASCADE, verbose_name='出题规则') rule = models.ForeignKey(TestRule, on_delete=models.CASCADE, verbose_name='所用规则', null=True, blank=True)
paper = models.ForeignKey(Paper, on_delete=models.CASCADE, verbose_name='所用试卷', null=True, blank=True)
consumer = models.ForeignKey(Consumer, on_delete=models.CASCADE, verbose_name='模考人') consumer = models.ForeignKey(Consumer, on_delete=models.CASCADE, verbose_name='模考人')
score = models.IntegerField(default=0, verbose_name='得分') total_score = models.FloatField(default=0, verbose_name='总分')
score = models.FloatField(default=0, verbose_name='得分')
took = models.IntegerField(default=0, verbose_name='耗时(秒)') took = models.IntegerField(default=0, verbose_name='耗时(秒)')
start_time = models.DateTimeField(verbose_name='开始答题时间') start_time = models.DateTimeField(verbose_name='开始答题时间')
end_time = models.DateTimeField(verbose_name='结束答题时间') end_time = models.DateTimeField(verbose_name='结束答题时间')
detail = models.ManyToManyField(Question, related_name='答题记录', through='AnswerDetail') detail = models.ManyToManyField(Question, related_name='答题记录', through='AnswerDetail')
is_pass = models.BooleanField(default=True, verbose_name='是否通过')
class Meta: class Meta:
verbose_name = '自助模考' verbose_name = '试表'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class AnswerDetail(CommonModel): class AnswerDetail(SoftCommonModel):
monitest = models.ForeignKey(MoniTest, on_delete=models.CASCADE) examtest = models.ForeignKey(ExamTest, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE)
user_answer = JSONField() user_answer = JSONField()
score = models.FloatField(default=0, verbose_name='本题得分') score = models.FloatField(default=0, verbose_name='本题得分')

View File

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from question.models import Questioncat from question.models import Questioncat
from .models import TestRule, MoniTest from crm.models import Consumer
from .models import TestRule, ExamTest, AnswerDetail
@ -41,12 +42,36 @@ class MoniTestSerializer(serializers.ModelSerializer):
""" """
自助模考序列化 自助模考序列化
""" """
start_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True) start_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True) end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
consumer = serializers.PrimaryKeyRelatedField(queryset=Consumer.objects.all(), required=False)
rule = serializers.PrimaryKeyRelatedField(queryset=TestRule.objects.all(), required=True)
create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True) create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True) update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
class Meta: class Meta:
model = MoniTest model = ExamTest
fields = '__all__' fields = '__all__'
class ExamTestListSerializer(serializers.ModelSerializer):
"""
考试列表序列化
"""
start_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
class Meta:
model = ExamTest
fields = '__all__'
class AnswerDetailSerializer(serializers.ModelSerializer):
class Meta:
model = AnswerDetail
fields = '__all__'
depth = 1
class AnswerDetailCreateSerializer(serializers.ModelSerializer):
class Meta:
model = AnswerDetail
fields = '__all__'

View File

@ -1,5 +1,5 @@
from django.urls import path,include from django.urls import path,include
from .views import TestRuleViewSet, MoniTestView from .views import TestRuleViewSet, MoniTestView, MyExamTestView, AnswerDetailView
from rest_framework import routers from rest_framework import routers
@ -8,5 +8,7 @@ router.register('testrule', TestRuleViewSet, basename="testrule")
urlpatterns = [ urlpatterns = [
path('monitest/',MoniTestView.as_view()), path('monitest/',MoniTestView.as_view()),
path('myexamtest/',MyExamTestView.as_view()),
path('answerdetail/', AnswerDetailView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -15,60 +15,65 @@ from utils.custom import CommonPagination
from rbac.permission import RbacPermission from rbac.permission import RbacPermission
from question.models import Question from question.models import Question
from question.serializers import QuestionSerializer from question.serializers import QuestionSerializer
from .models import TestRule from .models import TestRule, ExamTest, AnswerDetail
from .serializers import TestRuleSerializer, TestRuleListSerializer, TestRuleCreateSerializer, MoniTestSerializer from .serializers import TestRuleSerializer, TestRuleListSerializer, TestRuleCreateSerializer, MoniTestSerializer, AnswerDetailSerializer, ExamTestListSerializer, AnswerDetailCreateSerializer
from server import settings from server import settings
from crm.authentication import ConsumerTokenAuthentication from crm.authentication import ConsumerTokenAuthentication
from utils.custom import CommonPagination
# Create your views here. # Create your views here.
class MoniTestView(APIView): class MoniTestView(APIView):
authentication_classes = [ConsumerTokenAuthentication] authentication_classes = [ConsumerTokenAuthentication]
permission_classes = [] permission_classes = []
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
data = request.data serializer = MoniTestSerializer(data = request.data)
pass if serializer.is_valid():
def get(self, request, *args, **kwargs): instance = serializer.save(consumer = request.user)
''' if 'questions' in request.data:
生成模拟考试 questions = []
''' for i in request.data['questions']:
if request.query_params.get('rule', None): question = {}
ret = {} question['question'] = i['id']
testrule = TestRule.objects.get(id = request.query_params.get('rule')) question['examtest'] = instance.id
ret['name'] = '自助模考' + datetime.now().strftime('%Y%m%d%H%M') question['score'] = i['score']
ret['type'] = '自助模考' # 自助模拟考试 if 'user_answer' in i:
ret['rule'] = testrule.id question['user_answer'] = i['user_answer']
ret['limit'] = testrule.limit question['is_right'] = i['is_right']
ret['total_score'] = testrule.total_score questions.append(question)
ret['pass_score'] = testrule.pass_score serializer_detail = AnswerDetailCreateSerializer(data=questions, many=True)
ret['danxuan_count'] = testrule.danxuan_count if serializer_detail.is_valid():
ret['danxuan_score'] = testrule.danxuan_score serializer_detail.save()
ret['duoxuan_count'] = testrule.duoxuan_count return Response(MoniTestSerializer(instance).data,status=status.HTTP_200_OK)
ret['duoxuan_score'] = testrule.duoxuan_score
ret['panduan_count'] = testrule.panduan_count
ret['panduan_score'] = testrule.panduan_score
question_queryset = Question.objects.none()
queryset = Question.objects.filter(is_delete=0,questioncat__in = testrule.questioncat.all())
if ret['danxuan_count']:
danxuan = queryset.filter(type='单选').order_by('?')[:ret['danxuan_count']]
question_queryset = question_queryset | danxuan
if ret['duoxuan_count']:
duoxuan = queryset.filter(type='多选').order_by('?')[:ret['duoxuan_count']]
question_queryset = question_queryset | duoxuan
if ret['panduan_count']:
panduan = queryset.filter(type='判断').order_by('?')[:ret['panduan_count']]
question_queryset = question_queryset | panduan
questions = QuestionSerializer(instance=question_queryset.order_by('type'),many=True).data
for i in questions:
if i['type'] == 1:
i['total_score'] = ret['danxuan_score']
elif i['type'] == 2:
i['total_score'] = ret['duoxuan_score']
else: else:
i['total_score'] = ret['panduan_score'] return Response(serializer_detail.errors)
ret['questions'] = questions
return Response(ret) else:
return Response({'error':'答题记录不存在'})
else:
return Response(serializer.errors)
class MyExamTestView(APIView):
authentication_classes = [ConsumerTokenAuthentication]
permission_classes = []
def get(self, request, *args, **kwargs):
queryset = ExamTest.objects.filter(consumer=request.user)
pg = CommonPagination()
p = pg.paginate_queryset(queryset=queryset,request=request,view=self)
serializer = ExamTestListSerializer(instance=p,many=True)
return pg.get_paginated_response(serializer.data)
class AnswerDetailView(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, *args, **kwargs):
queryset = AnswerDetail.objects.all()
if request.query_params.get('examtest', None):
queryset = queryset.filter(examtest=request.query_params.get('examtest'))
serializer = AnswerDetailSerializer(instance=queryset,many=True)
return Response(serializer.data)
@ -114,4 +119,46 @@ class TestRuleViewSet(ModelViewSet):
""" """
if self.request.method == 'GET': if self.request.method == 'GET':
self.permission_classes = [] self.permission_classes = []
return [permission() for permission in self.permission_classes] return [permission() for permission in self.permission_classes]
@action(methods=['get'], detail=True, permission_classes=[IsAuthenticated],
url_path='monitest', url_name='gen_monitest')
def monitest(self, request, pk=None):
'''
生成自助模拟考试
'''
ret = {}
testrule = self.get_object()
ret['name'] = '自助模考' + datetime.now().strftime('%Y%m%d%H%M')
ret['type'] = '自助模考' # 自助模拟考试
ret['rule'] = testrule.id
ret['limit'] = testrule.limit
ret['total_score'] = testrule.total_score
ret['pass_score'] = testrule.pass_score
ret['danxuan_count'] = testrule.danxuan_count
ret['danxuan_score'] = testrule.danxuan_score
ret['duoxuan_count'] = testrule.duoxuan_count
ret['duoxuan_score'] = testrule.duoxuan_score
ret['panduan_count'] = testrule.panduan_count
ret['panduan_score'] = testrule.panduan_score
question_queryset = Question.objects.none()
queryset = Question.objects.filter(is_delete=0,questioncat__in = testrule.questioncat.all())
if ret['danxuan_count']:
danxuan = queryset.filter(type='单选').order_by('?')[:ret['danxuan_count']]
question_queryset = question_queryset | danxuan
if ret['duoxuan_count']:
duoxuan = queryset.filter(type='多选').order_by('?')[:ret['duoxuan_count']]
question_queryset = question_queryset | duoxuan
if ret['panduan_count']:
panduan = queryset.filter(type='判断').order_by('?')[:ret['panduan_count']]
question_queryset = question_queryset | panduan
questions = QuestionSerializer(instance=question_queryset.order_by('type'),many=True).data
for i in questions:
if i['type'] == 1:
i['total_score'] = ret['danxuan_score']
elif i['type'] == 2:
i['total_score'] = ret['duoxuan_score']
else:
i['total_score'] = ret['panduan_score']
ret['questions'] = questions
return Response(ret)

View File

@ -1,10 +1,10 @@
from django.db import models from django.db import models
from rbac.models import CommonModel from rbac.models import SoftCommonModel
from django.contrib.postgres.fields import JSONField, ArrayField from django.contrib.postgres.fields import JSONField, ArrayField
# Create your models here. # Create your models here.
class Questioncat(CommonModel): class Questioncat(SoftCommonModel):
name = models.CharField(max_length=200, unique=True, verbose_name='名称') name = models.CharField(max_length=200, unique=True, verbose_name='名称')
pid = models.ForeignKey('self', verbose_name='', null=True, blank=True, on_delete=models.CASCADE, related_name='questioncatpid') pid = models.ForeignKey('self', verbose_name='', null=True, blank=True, on_delete=models.CASCADE, related_name='questioncatpid')
is_subject = models.BooleanField(default=False, verbose_name='是否是学科') is_subject = models.BooleanField(default=False, verbose_name='是否是学科')
@ -20,7 +20,7 @@ class Questioncat(CommonModel):
return self.questioncat.count() return self.questioncat.count()
class Question(CommonModel): class Question(SoftCommonModel):
type_choices = ( type_choices = (
('单选', '单选'), ('单选', '单选'),
('多选', '多选'), ('多选', '多选'),

View File

@ -13,7 +13,7 @@ class SoftDeletManager(models.Manager):
''' '''
def get_queryset(self): def get_queryset(self):
""" """
在这里处理一下QuerySet, 然后返回没被标记位is_deleted的QuerySet 在这里处理一下QuerySet, 然后返回没被标记位is_delete的QuerySet
""" """
kwargs = {'model': self.model, 'using': self._db} kwargs = {'model': self.model, 'using': self._db}
if hasattr(self, '_hints'): if hasattr(self, '_hints'):
@ -21,7 +21,7 @@ class SoftDeletManager(models.Manager):
return self._queryset_class(**kwargs).filter(is_delete=False) return self._queryset_class(**kwargs).filter(is_delete=False)
class CommonModel(models.Model): class SoftCommonModel(models.Model):
create_time = models.DateTimeField(default=timezone.now, verbose_name='创建时间', help_text='创建时间') create_time = models.DateTimeField(default=timezone.now, verbose_name='创建时间', help_text='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='修改时间', help_text='修改时间') update_time = models.DateTimeField(auto_now=True, verbose_name='修改时间', help_text='修改时间')
is_delete = models.BooleanField(default=False, verbose_name='删除标记', help_text='删除标记') is_delete = models.BooleanField(default=False, verbose_name='删除标记', help_text='删除标记')
@ -39,10 +39,18 @@ class CommonModel(models.Model):
self.is_delete = True self.is_delete = True
self.save(using=using) self.save(using=using)
else: else:
return super(CommonModel, self).delete(using=using, *args, **kwargs) return super(SoftCommonModel, self).delete(using=using, *args, **kwargs)
class CommonModel(models.Model):
create_time = models.DateTimeField(default=timezone.now, verbose_name='创建时间', help_text='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='修改时间', help_text='修改时间')
is_delete = models.BooleanField(default=False, verbose_name='删除标记', help_text='删除标记')
class Meta:
abstract = True
class Menu(CommonModel): class Menu(SoftCommonModel):
""" """
功能权限:目录,菜单,权限 功能权限:目录,菜单,权限
""" """
@ -68,7 +76,7 @@ class Menu(CommonModel):
ordering = ['id'] ordering = ['id']
class Role(CommonModel): class Role(SoftCommonModel):
""" """
角色 角色
""" """
@ -83,7 +91,7 @@ class Role(CommonModel):
def __str__(self): def __str__(self):
return self.name return self.name
class Organization(CommonModel): class Organization(SoftCommonModel):
""" """
组织架构 组织架构
""" """