客观题页面

sale
tianea 3 years ago
parent a98e0ae083
commit 4d55468ca9

@ -0,0 +1,39 @@
import request from '@/utils/request'
let baseUrl = '/pc/course/'
export function getList(query) {
return request({
url: baseUrl+'list',
method: 'post',
data: query
})
}
export function add(cat) {
return request({
url: baseUrl+"add",
method: 'post',
data: cat
})
}
export function update(cat) {
return request({
url: baseUrl+"update",
method: 'post',
data: cat
})
}
export function del(cat) {
return request({
url: baseUrl+"delete",
method: 'post',
data: cat
})
}
export function get(cat) {
return request({
url: baseUrl+"get",
method: 'post',
data: cat
})
}

@ -0,0 +1,38 @@
import request from '@/utils/request'
let baseUrl = '/pc/system/level/'
export function getList() {
return request({
url: baseUrl+'list',
method: 'get'
})
}
export function add(cat) {
return request({
url: baseUrl+"add",
method: 'post',
data: cat
})
}
export function update(cat) {
return request({
url: baseUrl+"update",
method: 'post',
data: cat
})
}
export function del(cat) {
return request({
url: baseUrl+"delete",
method: 'post',
data: cat
})
}
export function get(cat) {
return request({
url: baseUrl+"get",
method: 'post',
data: cat
})
}

@ -0,0 +1,46 @@
import request from '@/utils/request'
let baseUrl = '/pc/question/'
export function getList(query) {
return request({
url: baseUrl+'list',
method: 'post',
data: query
})
}
export function add(cat) {
return request({
url: baseUrl+"add",
method: 'post',
data: cat
})
}
export function update(cat) {
return request({
url: baseUrl+"update",
method: 'post',
data: cat
})
}
export function del(cat) {
return request({
url: baseUrl+"delete",
method: 'post',
data: cat
})
}
export function get(cat) {
return request({
url: baseUrl+"get",
method: 'post',
data: cat
})
}
export function batch(data){
return request({
url: baseUrl+"batchupdate",
method: 'post',
data: data
})
}

@ -44,7 +44,6 @@ export default {
}
},
onload(){
myHeaders
},
computed: {
imageUrl() {
@ -56,26 +55,12 @@ export default {
this.emitInput('')
},
emitInput(val) {
this.$emit('input', val)
this.tempUrl = val
this.$emit('changeUrl', val)
},
handleImageSuccess() {
handleImageSuccess(response) {
this.tempUrl = response.data.url
this.emitInput(this.tempUrl)
},
beforeUpload() {
// const _self = this
return new Promise((resolve, reject) => {
// getToken().then(response => {
// const key = response.data.qiniu_key
// const token = response.data.qiniu_token
// _self._data.dataObj.token = token
// _self._data.dataObj.key = key
// this.tempUrl = response.data.qiniu_url
// resolve(true)
// }).catch(err => {
// console.log(err)
// reject(false)
// })
})
}
}
}
@ -106,7 +91,7 @@ export default {
width: 100%;
height: 100%;
img {
width: 100%;
// width: 100%;
height: 100%;
}
}

@ -7,16 +7,16 @@
:on-success="handleImageSuccess"
class="image-uploader"
drag
action="https://httpbin.org/post"
action="/api/upload/image/upload"
>
<i class="el-icon-upload" />
<div class="el-upload__text">
Drag或<em>点击上传</em>
</div>
</el-upload>
<div v-show="imageUrl.length>0" class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl">
<div v-show="url.length>0" class="image-preview">
<div v-show="url.length>1" class="image-preview-wrapper">
<img :src="url">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" />
</div>
@ -26,12 +26,12 @@
</template>
<script>
import { getToken } from '@/api/qiniu'
import { getToken } from '@/utils/auth'
export default {
name: 'SingleImageUpload2',
props: {
value: {
url: {
type: String,
default: ''
}
@ -39,12 +39,8 @@ export default {
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
}
},
computed: {
imageUrl() {
return this.value
dataObj: { w: 800, h: 600 },
myHeaders: {'Tz-Token':getToken()}
}
},
methods: {
@ -52,25 +48,10 @@ export default {
this.emitInput('')
},
emitInput(val) {
this.$emit('input', val)
},
handleImageSuccess() {
this.emitInput(this.tempUrl)
this.$emit('update:url', val)
},
beforeUpload() {
const _self = this
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
this.tempUrl = response.data.qiniu_url
resolve(true)
}).catch(() => {
reject(false)
})
})
handleImageSuccess(response) {
this.emitInput(response.data.url)
}
}
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645446191066" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2770" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M568.263111 96.028444c25.429333 0 49.834667 10.069333 67.868445 28.103112l199.736888 199.736888c18.033778 18.033778 28.16 42.439111 28.16 67.868445v408.234667a128 128 0 0 1-128 128H287.914667a128 128 0 0 1-128-128V224.028444a128 128 0 0 1 128-128H568.32z m0.398222 71.964445H287.971556a55.978667 55.978667 0 0 0-55.978667 55.068444v576.967111c0 30.606222 24.576 55.466667 55.068444 55.978667h448.910223a55.978667 55.978667 0 0 0 56.035555-55.068444V391.736889a24.007111 24.007111 0 0 0-6.712889-16.611556l-200.078222-200.078222a24.007111 24.007111 0 0 0-16.497778-7.054222z m-28.728889 500.053333a36.010667 36.010667 0 0 1 0 71.964445H355.953778a36.010667 36.010667 0 1 1 0-72.021334H539.875556z m39.480889-331.036444a36.010667 36.010667 0 0 1 0 50.915555l-190.407111 190.407111a1.991111 1.991111 0 0 1-1.251555 0.568889l-48.696889 3.356445a1.991111 1.991111 0 0 1-2.161778-1.877334v-0.170666l0.682667-51.427556c0-0.568889 0.227556-1.024 0.568889-1.422222L528.497778 337.009778a36.010667 36.010667 0 0 1 50.915555 0z" p-id="2771" fill="#d81e06"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645446491415" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1317" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M898.3 960.1H280.2c-34.1 0-61.8-27.7-61.8-61.8V280.2c0-34.1 27.7-61.8 61.8-61.8h618.1c34.1 0 61.8 27.7 61.8 61.8v618.1c0 34.2-27.6 61.8-61.8 61.8z m0-649c0-8.2-3.3-16.1-9.1-21.9-5.8-5.8-13.7-9.1-21.9-9.1H311.1c-17.1 0-30.9 13.8-30.9 30.9v556.3c0 8.2 3.3 16.1 9.1 21.9 5.8 5.8 13.7 9.1 21.9 9.1h556.3c8.2 0 16.1-3.3 21.9-9.1 5.8-5.8 9.1-13.7 9.1-21.9V311.1h-0.2z m-170 278.2H620.2v108.2c0 4.1-1.6 8-4.5 10.9-2.9 2.9-6.8 4.5-10.9 4.5h-30.9c-4.1 0-8-1.6-10.9-4.5-2.9-2.9-4.5-6.8-4.5-10.9V589.3H450.2c-8.5 0-15.5-6.9-15.5-15.5v-30.9c0-8.5 6.9-15.5 15.5-15.5h108.2V419.3c0-4.1 1.6-8 4.5-10.9 2.9-2.9 6.8-4.5 10.9-4.5h30.9c4.1 0 8 1.6 10.9 4.5 2.9 2.9 4.5 6.8 4.5 10.9v108.2h108.2c8.5 0 15.5 6.9 15.5 15.5v30.9c0 8.4-6.9 15.4-15.5 15.4z m46.4-401.8c-17.1 0-30.9-13.8-30.9-30.9 0-8.2-3.3-16.1-9.1-21.9-5.8-5.8-13.7-9.1-21.9-9.1H156.6c-8.2 0-16.1 3.3-21.9 9.1-5.8 5.8-9.1 13.7-9.1 21.9v556.3c0 8.2 3.3 16.1 9.1 21.9 5.8 5.8 13.7 9.1 21.9 9.1 17.1 0 30.9 13.8 30.9 30.9 0 17.1-13.8 30.9-30.9 30.9h-30.9c-34.1 0-61.8-27.7-61.8-61.8V125.7c0-34.1 27.7-61.8 61.8-61.8h618.1c34.1 0 61.8 27.7 61.8 61.8v30.9c0 8.2-3.3 16.1-9.1 21.9-5.7 5.7-13.6 9-21.8 9z" p-id="1318" fill="#f4ea2a"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645445980496" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2619" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M956.48 1012.032H73.408V10.048h624.704l258.368 254.08v747.904zM702.016 113.344v161.792h161.792l-161.792-161.792zM584.064 205.44H203.2v65.216h380.864V205.44z m243.84 185.408H202.112v65.152h625.792V390.848z" fill="#2F63AE" p-id="2620"></path><path d="M688.064 622.976c-14.912 0-27.2 1.856-36.736 5.76s-17.28 9.28-23.04 16.128-10.176 14.976-12.992 24.256a162.816 162.816 0 0 0-6.08 30.016l-59.584-12.544c2.048-18.496 6.592-35.328 13.44-50.368s16.128-27.968 27.712-38.592c11.712-10.56 25.536-18.752 41.664-24.384s34.624-8.512 55.552-8.512c17.856 0 34.752 2.56 50.624 7.616 15.808 5.056 29.568 12.544 41.216 22.4a108.8 108.8 0 0 1 27.84 36.032c6.784 14.208 10.24 30.208 10.24 48.192 0 15.168-2.56 28.16-7.808 38.912s-11.648 20.544-19.264 29.568-16 17.6-25.152 25.984c-9.088 8.384-17.408 17.6-25.088 27.776s-14.08 21.888-19.264 35.2a132.736 132.736 0 0 0-7.872 48.576h-60.928c0-14.848 1.024-28.544 2.944-40.96a145.28 145.28 0 0 1 9.6-34.304c6.592-11.904 14.848-22.848 24.896-32.896s19.648-20.096 28.864-30.272 17.216-20.608 23.744-31.36 9.856-22.848 9.856-36.224c0-9.856-1.6-18.368-4.928-25.536s-7.744-12.992-13.44-17.472a55.808 55.808 0 0 0-20.352-9.792 95.872 95.872 0 0 0-25.664-3.2z m-0.96 280.896c5.696 0 11.072 1.152 16.128 3.392a43.52 43.52 0 0 1 26.048 39.232 42.88 42.88 0 0 1-26.048 38.784 41.92 41.92 0 0 1-46.592-9.216 41.216 41.216 0 0 1-12.16-29.568 43.456 43.456 0 0 1 12.16-30.08 43.904 43.904 0 0 1 30.464-12.544z" fill="#FFFFFF" p-id="2621"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -34,6 +34,21 @@ if (process.env.NODE_ENV === 'production') {
Vue.use(ElementUI)
Vue.config.productionTip = false
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
new Vue({
el: '#app',

@ -146,9 +146,35 @@ export const constantRoutes = [
},
{
path: 'exam',
component: () => import('@/views/resource/exam/list'),
component: () => import('@/views/resource/exam/index'),
name: 'exam',
meta: { title: '客观题管理', icon: 'exam_normal'}
meta: { title: '客观题管理', icon: 'exam_normal'},
redirect: '/resource/exam/list',
children: [
{
path: 'list',
name: 'List',
component: () => import('@/views/resource/exam/list'),
meta: { title: '试题列表', icon: 'question'}
},
{
path: 'add',
name: 'Add',
component: () => import('@/views/resource/exam/add/index'),
meta: {title: '新增题目', icon: 'add_question'}
},
{
path: 'edit',
name: 'Edit',
component: () => import('@/views/resource/exam/add/edit')
},
{
path: 'batch',
name: 'Batch',
component: () => import('@/views/resource/exam/add/batch'),
meta: {title: '批量导入', icon: 'batch_add'}
}
]
}
]
},

@ -1,29 +1,30 @@
<template>
<div class="app-container">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="margin-left:50px;">
<el-form-item label="课程名称" prop="type">
<el-input :model="temp.name" />
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="margin-left:50px;">
<el-form-item label="课程名称" prop="name">
<el-input v-model="temp.name" />
</el-form-item>
<el-form-item label="课程缩略图">
<upload-image :mode="temp.url" />
<upload-image v-model="temp.thumbnail" @changeUrl="changeUrl" />
</el-form-item>
<el-form-item label="课程类别" prop="timestamp">
<el-select v-model="temp.cat" class="filter-item" placeholder="请选择">
<el-select v-model="temp.catId" class="filter-item" placeholder="请选择">
<el-option v-for="item in catOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="课程标签" prop="timestamp">
<el-select v-model="temp.tag" class="filter-item" placeholder="请选择">
<el-select v-model="temp.tagId" class="filter-item" placeholder="请选择">
<el-option v-for="item in tagOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="学习人数">
<el-input v-model="temp.creator" />
<el-input v-model="temp.learningCount" />
</el-form-item>
<el-form-item label="课程简介">
<editor
api-key="wuyv0zkbaek2eni7uc4cj2q099vfhbibrj3cv5yjymq41bod"
initialValue=""
v-model="temp.content"
:init="{
height: 500,
menubar: false,
@ -47,7 +48,7 @@
<el-button @click="dialogFormVisible = false">
取消
</el-button>
<el-button type="primary">
<el-button type="primary" @click="add">
确认
</el-button>
</div>
@ -55,6 +56,7 @@
<script>
import * as tag from '@/api/courseTag'
import * as cat from '@/api/courseCat'
import * as course from '@/api/course'
import uploadImage from '@/components/Upload/SingleImage.vue'
import Editor from '@tinymce/tinymce-vue'
@ -73,8 +75,6 @@ export default {
return statusMap[status]
}
},
onload(){
},
data() {
return {
list: null,
@ -94,18 +94,23 @@ export default {
dialogStatus: 'Edit',
statusOptions: ['published', 'draft', 'deleted'],
temp: {
name: '',
url: '',
content: ''
},
rules: {
studentNo: [{ required: true, message: '学号为必填项', trigger: 'blur' }],
name: [{ required: true, message: '姓名为必填项', trigger: 'blur' }],
mobile: [{ required: true, message: '电话为必填项', trigger: 'blur' }],
school: [{ required: true, message: '学校为必填项', trigger: 'blur' }]
name: [{ required: true, message: '课程名为必填项', trigger: 'blur' }]
}
}
},
created() {
if(this.$route.query){
if(this.$route.query.id){
course.get({id: this.$route.query.id}).then(res=>{
this.temp= res.data
})
}
}
this.fetchData()
},
methods: {
@ -116,9 +121,8 @@ export default {
})
cat.getList().then(res=>{
this.catOptions = res.data.list
this.listLoading =false
this.listLoading = false
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
@ -128,6 +132,21 @@ export default {
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
add(){
course.add(this.temp).then(response=>{
if(response.code === 200){
this.$notify({
title: '操作成功',
message: "success",
type: 'success',
duration: 2000
})
}
})
},
changeUrl(val){
this.temp.thumbnail = val
}
}
}

@ -19,12 +19,12 @@
</el-table-column>
<el-table-column label="课程类别">
<template slot-scope="scope">
{{ scope.row.type }}
{{ catIdToName(scope.row.catId) }}
</template>
</el-table-column>
<el-table-column label="课程ID" align="center">
<el-table-column label="标签" align="center">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
<span>{{ tagIdToName(scope.row.tagId) }}</span>
</template>
</el-table-column>
<el-table-column label="创建人" align="center">
@ -39,7 +39,7 @@
</el-table-column>
<el-table-column align="center" label="操作" width="300" class-name="small-padding fixed-width">
<template slot-scope="{row,$index}">
<el-button type="primary" size="mini" @click="handleUpdate(row)">
<el-button type="primary" size="mini" @click="gotoEdit(row)">
编辑
</el-button>
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
@ -56,13 +56,13 @@
:total="total"
/>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="课程名称" prop="type">
<el-input v-model="temp.name" />
</el-form-item>
<el-form-item label="课程类别" prop="timestamp">
<el-select v-model="temp.type" class="filter-item" placeholder="请选择">
<el-option v-for="item in provincOtions" :key="item.id" :label="item.name" :value="item.key" />
<el-select v-model="temp.catId" class="filter-item" placeholder="请选择">
<el-option v-for="item in catOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="课程ID" prop="title">
@ -88,8 +88,9 @@
</template>
<script>
import { getList } from '@/api/table'
import { getList ,del } from '@/api/course'
import * as tag from '@/api/courseTag'
import * as cat from '@/api/courseCat'
export default {
filters: {
statusFilter(status) {
@ -115,18 +116,8 @@ export default {
create: 'Create'
},
dialogFormVisible: false,
provincOtions: [{
id: '1',
name: '北京'
}],
regionOptions: [{
id: 1,
name: '东南地区'
}],
levelOtions: [{
id: 1,
name: '本科'
}],
tagOptions: [],
catOptions: [],
dialogStatus: '',
statusOptions: ['published', 'draft', 'deleted'],
temp: {
@ -145,16 +136,23 @@ export default {
}
}
},
created() {
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
tag.getList().then(res=>{
this.tagOptions = res.data.list
})
cat.getList().then(res=>{
this.catOptions = res.data.list
this.listLoading = false
})
getList(this.listQuery).then(response => {
const userPage = response.data.userPage
this.list = userPage.content
this.total = userPage.totalElements
const onePage = response.data.page
this.list = onePage.content
this.total = onePage.totalElements
this.listLoading = false
})
},
@ -166,6 +164,25 @@ export default {
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
handleDelete(item,index){
del(item).then(response => {
if(response.code === 200){
this.list.splice(index,1)
}
})
},
catIdToName(catId){
return this.catOptions.find(cat=>cat.id == catId).name
},
tagIdToName(tagId){
return this.tagOptions.find(tag=>tag.id === tagId).name
},
gotoEdit(row){
this.$router.push({
path: '/resource/course/add',
query: {id: row.id}
})
}
}
}

@ -0,0 +1,261 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-button @click="$router.back()">
取消
</el-button>
<el-button type="primary" @click="add">
确认
</el-button>
</div>
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="margin-left:50px;">
<div class="tz-flex">
<el-form-item label="归属课程" >
<el-select v-model="temp.courseId" class="filter-item" placeholder="请选择">
<el-option v-for="item in courseOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="题目层次">
<el-select v-model="temp.levelId" class="filter-item" placeholder="请选择">
<el-option v-for="item in levelOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="题型" prop="timestamp">
<el-select @change="typeChange" v-model="temp.questionType" class="filter-item" placeholder="请选择">
<el-option v-for="item in typeOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="分值" prop="timestamp">
<el-input-number v-model="temp.score" :min="1" />
</el-form-item>
</div>
<el-form-item label="题干">
<el-input type="textarea" v-model="temp.stem" prop="stem" />
<el-checkbox v-model="checkGroup.stem" label="图" />
<upload-image v-if="checkGroup.stem" v-model="temp.stemImg" :url.sync="temp.stemImg" />
</el-form-item>
<el-form-item label="选项">
<div v-for="item in temp.answerList" :key="item.id" class="opt">
<div class="line">
<span class="correct" @dblclick="cancel(item)" @click="confirm(item)" :style="item.correct?'background-color:red; color:white':''" >{{ indexToWord(item.id) }}</span> <el-input v-model="item.title" /><el-checkbox v-if="temp.questionType<3" v-model="item.check" label="图" />
</div>
<div class="line">
<upload-image v-if="!!item.img" :url.sync="item.img" />
</div>
</div>
</el-form-item>
<el-form-item label="答案">
<div style="display:flex">
<span class="correct" style="background-color:red; color:white" v-for="item in temp.answerId" :key="item">{{ indexToWord(item) }}</span>
</div>
</el-form-item>
<el-form-item label="解析">
<el-input type="textarea" v-model="temp.analysis" />
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as course from '@/api/course'
import * as level from '@/api/level'
import * as question from '@/api/question'
import uploadImage from '@/components/Upload/SingleImage2.vue'
export default {
components: {
'upload-image': uploadImage
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
listQuery: {
pageNo: 0
},
total: 0,
limit: 20,
textMap: {
update: 'Edit',
create: 'Create'
},
checkGroup:{
stem: false
},
dialogFormVisible: false,
courseOptions: [],
levelOptions: [],
typeOptions:[
{
id: 1,
name: '单选'
},
{
id: 2,
name: '多选'
},
{
id: 3,
name: '判断'
}
],
dialogStatus: 'Edit',
statusOptions: ['published', 'draft', 'deleted'],
temp: {
score: 1,
courseId: 0,
courseName: '',
levelId: 1,
levelName: '',
questionType: 1,
stem: '',
stemImg: '',
answerList:[],
answerId: [],
analysis: ''
},
rules: {
stem: [{ required: true, message: '题干为必填项', trigger: 'blur' }]
}
}
},
created() {
if(this.$route.query){
if(this.$route.query.id){
question.get({id: this.$route.query.id}).then(res=>{
this.temp= res.data
this.checkGroup.stem = !!this.temp.stemImg
this.temp.answerId.forEach(i=>this.temp.answerList.find( (t)=> t.id == i ).correct=true)
this.temp.answerId.forEach(i=>this.temp.answerList.find( (t)=> t.id == i ).check=!!this.temp.answerList.find( (t)=> t.id == i ).img)
})
}
}
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
course.getList({pageNo: 0, pageSize: 1000}).then(res=>{
if(res.code==200){
this.courseOptions = res.data.page.content
}
this.listLoading = false
})
level.getList().then(res=>{
if(res.code==200){
this.levelOptions = res.data.list
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
add(){
question.add(this.temp).then(response=>{
if(response.code === 200){
this.$notify({
title: '操作成功',
message: "success",
type: 'success',
duration: 2000
})
}
})
},
changeUrl(val){
this.temp.thumbnail = val
},
typeChange(val){
this.temp.answerList=[]
this.temp.answerId=[]
if(val == 1||val == 2){
this.temp.answerList.push({ id: 1, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 2, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 3, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 4, title: '', img: '', check: false, correct: false });
}else{
this.temp.answerList.push({ id: 1, title: '正确', img: '', correct: false});
this.temp.answerList.push({ id: 2, title: '错误', img: '', correct: false});
}
},
indexToWord(index){
return String.fromCharCode(64+index)
},
cancel(item){
item.correct = false
const i = this.temp.answerId.indexOf(item.id)
this.temp.answerId.splice(i,1)
},
confirm(item){
if(this.temp.questionType !=2){
this.temp.answerList.forEach(i =>{ i.correct = false})
this.temp.answerId = []
}
item.correct = true
if(this.temp.answerId.includes(item.id) == false){
this.temp.answerId.push(item.id)
}
}
}
}
</script>
<style scoped>
.tz-line{
display: flex;
width: 100%;
margin-bottom: 20px;
}
.tz-line button{
margin-left: 10px;
width: 100px;
}
.tz-flex{
display: flex;
flex-wrap: wrap;
}
.tz-flex .el-form-item,.el-select{
width: 50%;
}
.opt{
margin-bottom: 5px;
margin-right: 5px;
}
.correct{
display: flex;
width: 50px;
background-color: rgb(209, 208, 205);
align-items: center;
justify-content: center;
margin-right:5px;
border-radius: 5px;
color:white;
}
.opt .el-input{
margin-left: 5px;
margin-right: 5px;
}
.opt .el-checkbox{
margin-left: 5px;
}
.opt div{
display: flex;
width: 100%;
margin-top: 5px;
}
</style>

@ -0,0 +1,259 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-button @click="$router.back()">
取消
</el-button>
<el-button type="primary" @click="add">
确认
</el-button>
</div>
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="margin-left:50px;">
<div class="tz-flex">
<el-form-item label="归属课程" prop="name">
<el-select v-model="temp.courseId" class="filter-item" placeholder="请选择">
<el-option v-for="item in courseOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="题目层次">
<el-select v-model="temp.levelId" class="filter-item" placeholder="请选择">
<el-option v-for="item in levelOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="题型" prop="timestamp">
<el-select @change="typeChange" v-model="temp.questionType" class="filter-item" placeholder="请选择">
<el-option v-for="item in typeOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="分值" prop="timestamp">
<el-input-number v-model="temp.score" :min="1" />
</el-form-item>
</div>
<el-form-item label="题干">
<el-input type="textarea" v-model="temp.stem" />
<el-checkbox v-model="checkGroup.stem" label="图" />
<upload-image v-if="checkGroup.stem" v-model="temp.stemImg" :url.sync="temp.stemImg" />
</el-form-item>
<el-form-item label="选项">
<div v-for="item in temp.answerList" :key="item.id" class="opt">
<div class="line">
<span class="correct" @dblclick="cancel(item)" @click="confirm(item)" :style="item.correct?'background-color:red; color:white':''" >{{ indexToWord(item.id) }}</span> <el-input v-model="item.title" /><el-checkbox v-if="temp.questionType<3" v-model="item.check" label="图" />
</div>
<div class="line">
<upload-image v-if="item.check" :url.sync="item.img" />
</div>
</div>
</el-form-item>
<el-form-item label="答案">
<div style="display:flex">
<span class="correct" style="background-color:red; color:white" v-for="item in temp.answerId" :key="item">{{ indexToWord(item) }}</span>
</div>
</el-form-item>
<el-form-item label="解析">
<el-input type="textarea" v-model="temp.analysis" />
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as course from '@/api/course'
import * as level from '@/api/level'
import * as question from '@/api/question'
import uploadImage from '@/components/Upload/SingleImage2.vue'
export default {
components: {
'upload-image': uploadImage
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
listQuery: {
pageNo: 0
},
total: 0,
limit: 20,
textMap: {
update: 'Edit',
create: 'Create'
},
checkGroup:{
stem: false
},
dialogFormVisible: false,
courseOptions: [],
levelOptions: [],
typeOptions:[
{
id: 1,
name: '单选'
},
{
id: 2,
name: '多选'
},
{
id: 3,
name: '判断'
}
],
dialogStatus: 'Edit',
statusOptions: ['published', 'draft', 'deleted'],
temp: {
score: 1,
courseId: 0,
courseName: '',
levelId: 1,
levelName: '',
questionType: 1,
stem: '',
stemImg: '',
answerList:[],
answerId: [],
analysis: ''
},
rules: {
name: [{ required: true, message: '课程名为必填项', trigger: 'blur' }]
}
}
},
created() {
if(this.$route.query){
if(this.$route.query.id){
course.get({id: this.$route.query.id}).then(res=>{
this.temp= res.data
})
}
}
this.fetchData()
this.typeChange(1)
},
methods: {
fetchData() {
this.listLoading = true
course.getList({pageNo: 0, pageSize: 1000}).then(res=>{
if(res.code==200){
this.courseOptions = res.data.page.content
}
this.listLoading = false
})
level.getList().then(res=>{
if(res.code==200){
this.levelOptions = res.data.list
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
add(){
question.add(this.temp).then(response=>{
if(response.code === 200){
this.$notify({
title: '操作成功',
message: "success",
type: 'success',
duration: 2000
})
}
})
},
changeUrl(val){
this.temp.thumbnail = val
},
typeChange(val){
this.temp.answerList=[]
this.temp.answerId=[]
if(val == 1||val == 2){
this.temp.answerList.push({ id: 1, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 2, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 3, title: '', img: '', check: false, correct: false });
this.temp.answerList.push({ id: 4, title: '', img: '', check: false, correct: false });
}else{
this.temp.answerList.push({ id: 1, title: '正确', img: '', correct: false});
this.temp.answerList.push({ id: 2, title: '错误', img: '', correct: false});
}
},
indexToWord(index){
return String.fromCharCode(64+index)
},
cancel(item){
item.correct = false
const i = this.temp.answerId.indexOf(item.id)
this.temp.answerId.splice(i,1)
},
confirm(item){
if(this.temp.questionType !=2){
this.temp.answerList.forEach(i =>{ i.correct = false})
this.temp.answerId = []
}
item.correct = true
if(this.temp.answerId.includes(item.id) == false){
this.temp.answerId.push(item.id)
}
}
}
}
</script>
<style scoped>
.tz-line{
display: flex;
width: 100%;
margin-bottom: 20px;
}
.tz-line button{
margin-left: 10px;
width: 100px;
}
.tz-flex{
display: flex;
flex-wrap: wrap;
}
.tz-flex .el-form-item,.el-select{
width: 50%;
}
.opt{
margin-bottom: 5px;
margin-right: 5px;
}
.correct{
display: flex;
width: 50px;
background-color: rgb(209, 208, 205);
align-items: center;
justify-content: center;
margin-right:5px;
border-radius: 5px;
color:white;
}
.opt .el-input{
margin-left: 5px;
margin-right: 5px;
}
.opt .el-checkbox{
margin-left: 5px;
}
.opt div{
display: flex;
width: 100%;
margin-top: 5px;
}
</style>

@ -3,19 +3,23 @@
<div class="filter-container">
<el-form>
<el-form-item label="课程筛选">
<el-select>
<el-option>请选择</el-option>
<el-select v-model="temp.course" >
<el-option v-for="item in cousrseOptions" :key="item.id" :value="item.name">{{ item.name }}</el-option>
</el-select>
<el-input placeholder="题干/题目ID" />
<el-input style="width: 200px;" placeholder="题干/题目ID" />
<el-button>查询</el-button>
<el-button>批量删除</el-button>
</el-form-item>
</el-form>
<el-button>新增</el-button>
<el-button @click="$router.push({ path: '/resource/exam/add'})" >新增</el-button>
<el-button>批量导入</el-button>
<el-button>批量上架</el-button>
<el-button>批量下架</el-button>
<el-button @click="batch(1)"></el-button>
<el-button @click="batch(0)"></el-button>
</div>
<div class="tz-line">
<el-checkbox label="全选" @change="selectAll" />
</div>
<el-table
v-loading="listLoading"
@ -25,55 +29,53 @@
fit
highlight-current-row
>
<el-table-column label="序号" align="center">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
<el-table-column label="选择" width="60px" align="center">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.checked" :key="scope.row.id" />
</template>
</el-table-column>
<el-table-column label="选择">
<el-table-column label="序号" align="center">
<template slot-scope="scope">
{{ scope.row.type }}
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column label="题干" align="center">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
<span>{{ scope.row.stem }}</span>
</template>
</el-table-column>
<el-table-column label="导入日期" align="center">
<template slot-scope="scope">
<span>{{ scope.row.creator }}</span>
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column label="题型" align="center">
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
<span>{{ typeToName(scope.row.questionType) }}</span>
</template>
</el-table-column>
<el-table-column label="题目层次" align="center">
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
<span>{{ levelToName(scope.row.levelId) }}</span>
</template>
</el-table-column>
<el-table-column label="归属课程" align="center">
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
<span>{{ courseToName(scope.row.courseId) }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="300" class-name="small-padding fixed-width">
<el-table-column align="center" label="操作" width="350" class-name="small-padding fixed-width">
<template slot-scope="{row,$index}">
<el-button type="primary" size="mini" @click="handleUpdate(row)">
<el-button type="primary" size="mini" @click="updateStatus(row,1)">
上架
</el-button>
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="updateStatus(row,0)">
下架
</el-button>
<el-button type="primary" size="mini" @click="handleUpdate(row)">
查看
</el-button>
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
修改
</el-button>
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
删除
</el-button>
@ -87,40 +89,13 @@
layout="total,prev,pager,next,jumper,sizes"
:total="total"
/>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
<el-form-item label="课程名称" prop="type">
<el-input v-model="temp.name" />
</el-form-item>
<el-form-item label="课程类别" prop="timestamp">
<el-select v-model="temp.type" class="filter-item" placeholder="请选择">
<el-option v-for="item in provincOtions" :key="item.id" :label="item.name" :value="item.key" />
</el-select>
</el-form-item>
<el-form-item label="课程ID" prop="title">
<el-input v-model="temp.id" />
</el-form-item>
<el-form-item label="创建人">
<el-input v-model="temp.creator" />
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="temp.createTime" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">
取消
</el-button>
<el-button type="primary">
确认
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getList } from '@/api/table'
import * as course from '@/api/course'
import * as level from '@/api/level'
import * as question from '@/api/question'
export default {
filters: {
@ -135,10 +110,12 @@ export default {
},
data() {
return {
list: null,
list: [],
listLoading: true,
listQuery: {
pageNo: 0
pageNo: 0,
courseId: 0,
stem: ''
},
total: 0,
limit: 20,
@ -146,35 +123,36 @@ export default {
update: 'Edit',
create: 'Create'
},
checkAll: false,
cMap: new Map(),
isIndeterminate: true,
dialogFormVisible: false,
provincOtions: [{
id: '1',
name: '北京'
}],
regionOptions: [{
id: 1,
name: '东南地区'
}],
levelOtions: [{
id: 1,
name: '本科'
}],
cousrseOptions:[],
levelOption:[],
checkList:[],
dialogStatus: '',
statusOptions: ['published', 'draft', 'deleted'],
temp: {
id: undefined,
importance: 1,
remark: '',
timestamp: new Date(),
title: '',
type: '',
status: 'published'
},
rules: {
type: [{ required: true, message: 'type is required', trigger: 'change' }],
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
title: [{ required: true, message: 'title is required', trigger: 'blur' }]
}
},
typeOptions:[
{
type: 1,
name: '单选'
},
{
type: 2,
name: '多选'
},
{
type: 3,
name: '判断'
}
]
}
},
created() {
@ -183,21 +161,114 @@ export default {
methods: {
fetchData() {
this.listLoading = true
getList(this.listQuery).then(response => {
const userPage = response.data.userPage
this.list = userPage.content
this.total = userPage.totalElements
question.getList(this.listQuery).then(res => {
if(res.code==200){
const onePage = res.data.page
this.list = onePage.content
for(var i =0; i<this.list.length;i++){
this.list.checked = true;
}
this.total = onePage.totalElements
}
this.listLoading = false
})
course.getList({pageNo: 0,pageSize:100}).then(res=>{
this.cousrseOptions = res.data.page.content;
})
level.getList().then(res=>{
if(res.code == 200){
this.levelOption = res.data.list
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
this.$router.push({
path: '/resource/exam/edit',
query: {id: row.id}
})
},
handleDelete(item,index){
question.del(item).then(response => {
if(response.code === 200){
this.list.splice(index,1)
}
})
},
updateStatus(item,status){
item.status = status
question.update(item).then(response=>{
if(response.code === 200){
if(status==0){
this.$notify({
title: '下架成功',
message: "success",
type: 'success',
duration: 2000
})
}else{
this.$notify({
title: '上架成功',
message: "success",
type: 'success',
duration: 2000
})
}
}
})
},
typeToName(type){
const t = this.typeOptions.find((t) => { return t.type = type})
if(t){
return this.typeOptions.find((t) => { return t.type = type}).name
}
return ''
},
levelToName(level){
if(this.levelOption.length>0){
const l = this.levelOption.find((l) => { return l.id == level })
if(l){
return l.name
}
}
return ''
},
courseToName(id){
if(this.levelOption.length>0 ){
const c = this.cousrseOptions.find((l) => { return l.id == level })
if(c){
return c.name
}
}
},
selectAll(val){
this.list.forEach(i => i.checked = val)
},
batch(stat){
let ids =[]
this.list.filter(item => item.checked).forEach( item => ids.push(item.id))
console.log(ids)
if(ids.length>0){
question.batch({ids:ids,status: stat}).then(res=>{
if(res.code==200){
if(stat==0){
this.$notify({
title: '下架成功',
message: "success",
type: 'success',
duration: 2000
})
}else{
this.$notify({
title: '上架成功',
message: "success",
type: 'success',
duration: 2000
})
}
}
})
}
}
}
}
@ -206,7 +277,8 @@ export default {
.tz-line{
display: flex;
width: 100%;
margin-bottom: 20px;
margin-bottom: 10px;
margin-top: 10px;
}
.tz-line button{
margin-left: 10px;

@ -0,0 +1,95 @@
package com.tz.platform.pc.biz;
import com.tz.platform.common.core.base.Result;
import com.tz.platform.entity.Question;
import com.tz.platform.pc.dto.PageQuestionDTO;
import com.tz.platform.pc.dto.QuestionDTO;
import com.tz.platform.pc.vo.BatchQuestionVO;
import com.tz.platform.pc.vo.PageQuestionVO;
import com.tz.platform.pc.vo.QuestionVO;
import com.tz.platform.repository.QuestionDao;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class PCQuestionBiz {
@Autowired
private QuestionDao questionDao;
public Result<PageQuestionDTO> list(PageQuestionVO questionVO){
PageQuestionDTO questionDTO = new PageQuestionDTO();
Pageable pageable = PageRequest.of(questionVO.getPageNo(),20);
Page<Question> questions =null;
if((questionVO.getCourseId() == null|| questionVO.getCourseId() == 0) && StringUtils.isEmpty(questionVO.getStem())){
questions = questionDao.findAll(pageable);
}else if(questionVO.getCourseId()>0&&StringUtils.hasText(questionVO.getStem())){
questions = questionDao.findAllByCourseIdAndStem(questionVO.getCourseId(),questionVO.getStem(),pageable);
}else if(questionVO.getCourseId()>0){
questions = questionDao.findAllByCourseId(questionVO.getCourseId(),pageable);
}else {
questions = questionDao.findAllByStem(questionVO.getStem(),pageable);
}
return Result.success(questionDTO.setPage(questions));
}
public Result<Long> add(QuestionVO questionVo){
Result<Long> v = valid(questionVo);
if(v.getCode()!=200){
return v;
}
Question question = new Question();
BeanUtils.copyProperties(questionVo,question);
question = questionDao.save(question);
return Result.success(question.getId());
}
public Result<String> update(QuestionVO questionVO){
Result<Long> v = valid(questionVO);
if(v.getCode()!=200){
return Result.error(v.getMsg());
}
Question question = new Question();
BeanUtils.copyProperties(questionVO,question);
questionDao.save(question);
return Result.success("success");
}
private Result<Long> valid(QuestionVO questionVO){
if(!StringUtils.hasText(questionVO.getStem())){
return Result.error("题干不能为空");
}
if(!StringUtils.hasText(questionVO.getAnalysis())){
return Result.error("解析不能为空");
}
// if(!StringUtils.hasText(questionVO.getCourseName())){
// return Result.error("课程名不能为空");
// }
return Result.success(0L);
}
public Result<String> delete(QuestionVO questionVO){
questionDao.deleteById(questionVO.getId());
return Result.success("success");
}
public Result<QuestionDTO> get(QuestionVO questionVO){
Question question = questionDao.getById(questionVO.getId());
if(question == null){
return Result.error("没有数据");
}
QuestionDTO questionDTO = new QuestionDTO();
BeanUtils.copyProperties(question,questionDTO);
return Result.success(questionDTO);
}
public Result<String> batch(BatchQuestionVO vo){
questionDao.batchUpdate(vo.getStatus(),vo.getIds());
return Result.success("success");
}
}

@ -4,11 +4,28 @@ import com.tz.platform.entity.Question;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Repository
public interface QuestionDao extends JpaRepository<Question,Long> {
Question getById(Long id);
Page<Question> findAll(Pageable pageable);
Page<Question> findAllByCourseId(Long courseId,Pageable pageable);
Page<Question> findAllByStem(String stem,Pageable pageable);
Page<Question> findAllByCourseIdAndStem(Long courseId,String stem,Pageable pageable);
@Transactional
@Modifying
@Query(value = "update question set status=:status where id in (:ids)",nativeQuery = true)
void batchUpdate(@Param("status") int status,@Param("ids") List<Long> ids);
}

Loading…
Cancel
Save