对于学生党来说,最常见的莫过于试卷考试题,调查问卷测试题等等,有没有一款论文是管理考试题库的?对于新手刚入门的同学来说,仔细看本文,保证你看完后可以自己手撸代码。
1、试卷题
试卷题型有很多种,常见的有单选、多选、填空、图片题等等,例如下图的问卷调查题。
2 题型组件定义
这么多题型,其界面和操作形式各有异同,因此我们可以简单的按照题型进行设计组件。
如下组织形式,我们先建立WQuestionItem 文件夹,然后添加各型号的题型组件:
- ImageSingleItem : 图片单选题
- MultiItem: 多选题
- SingleItem: 单选题
- TextItem: 填空题
- NumItem: 数字题
- …
2.1 设计单选题型组件
万事开头难,如果我们能顺利的设计好单选题型,那么多选题型可能仅仅是选择和结果不同而已。
因此在设计单选题组件时,我们需要费点力气。
好了,看看设计的组件类图。
这里单选题型组件作为一个响应输入,返回结果的组件,最最适合实现其v-model
的双向绑定行为。我们都试过ElementUI的各个vue组件,那个设计的溜啊,这里我们也借鉴以下。
vue官方解释:
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。利用model 选项可以用来避免这样的冲突。
2.1.1 模板设计
我们采用ElementUI来作为主要的输出UI。在布局上使用el-card
,刚好把试题的标题放在header区,把选项放在数据区。
<template>
<div>
<el-card class="box-card" :shadow="shadow" :body-style="bodyStyle">
<div slot="header" class="clearfix">
<el-row type="flex" justify="space-between">
<el-col :span="20">
<h3>{{ question.title }}</h3>
</el-col>
<el-col :span="4" style="width:300px;text-align:right;">
</el-col>
</el-row>
</div>
<el-form
ref="form"
:model="answer"
label-width="20px"
label-position="top"
size="medium"
class="question"
>
<template>
<el-form-item>
<el-radio-group ref="rg" :value="answer.content" @input="handleInput">
<el-col
v-for="(item, index) in question.items"
:key="index"
:class="className"
style="margin-bottom:20px;"
@mouseover.native="hover($event)"
@mouseout.native="removeClass($event)"
>
<el-radio :label="item.index">{{ item.content }}</el-radio>
</el-col>
</el-radio-group>
</el-form-item>
</template>
</el-form>
</el-card>
</div>
</template>
这里使用@mouseover.native="hover($event)" @mouseout.native="removeClass($event)"
完成鼠标滑过选项的效果。
2.1.2 组件代码实现1
可以预见的是,组件内必然有部分代码需要公用,因此我们设计了混合类,来复用功能。
export const CommonItem = {
data() {
return {
className: '',
activeName: 0,
}
},
created() {
// console.log('这是混入对象的created')
},
methods: {
hover(event) {
event.currentTarget.className = 'bold'
},
removeClass(event) {
event.currentTarget.className = ''
},
},
computed: {
shadow() {
return this.question.child ? 'never' : 'always'
},
bodyStyle() {
return this.question.child ? 'min-height:320px ;background-color: transparent'
: 'min-height:320px ;background: -webkit-linear-gradient(top,rgba(234, 240, 184, 0.212),rgba(224, 221, 172, 0.507),rgb(234, 240, 184)) no-repeat;'
},
},
}
2.1.3 组件代码实现2
混合类作为基础打底,也可以随时把公用代码提取到混合类里。
现在该到了单选组件实现的代码了。
// 引用混合类
import { CommonItem } from './mixins'
export default {
name: 'SingleItem',
mixins: [CommonItem],
//重定义model名,我们接收到answer
model: {
prop: 'answer',
event: 'input'
},
props: {
// 题
question: {
type: Object,
required: true,
default: () => {
return {}
}
},
answer: {
type: Object
},
},
}
再设计方法,主要处理输入事件。
注意:选项的值放在 answer.content
。然后组织好值后,触发组件的时间input this.$emit('input', rtn)
。
methods: {
handleInput(value) {
var rtn = {
...this.answer,
content: value,
state: value >= 0
}
console.log('single handleInput:', rtn)
this.$emit('input', rtn)
},
handleKeyDown(e) {
// console.log('key:',e.keyCode)
if (e.keyCode > 48 && e.keyCode <= 57) {
// 1 答案0
var index = (e.keyCode - 49)
if (this.question.items.length > index && index >= 0) {
var ans = index.toString()
this.handleInput(ans)
}
e.preventDefault()
}
},
handleKeyUp(e) {
},
}
2.2 填空题组件
既然已经搞定了单项选择题,那么我们就开始填空题组件,应为它比较简单,柿子先从软的捏。
复制单选择题组件,修改为TextItem.vue.
替换模板中的el-form
:
<el-form
ref="form"
:model="answer"
label-width="20px"
label-position="top"
size="medium"
class="question"
>
<template>
<el-form-item label="">
<el-input
type="textarea"
:value="textVal"
@input="handleInput"
/>
</el-form-item>
</template>
</el-form>
2.2.1 定义组件代码
增加data:
data() {
return {
textVal: Array.isArray(this.answer.content) ? '' : this.answer.content
}
},
2.2.2 处理输入
handleInput(value) {
var rtn = {
...this.answer,
content: value,
state: value.length > 0
}
console.log('text handleInput:', rtn)
this.$emit('input', rtn)
},
2.3 多选题组件
利用Ctrl+C、Ctrl+V大法,修改el-form
代码:
<el-form
ref="form"
:model="answer"
label-width="20px"
label-position="top"
size="medium"
class="question"
>
<template>
<el-form-item>
<el-checkbox-group :value="answer.content" @input="handleInput">
<el-col
v-for="(item, index) in question.items"
:key="index"
style="margin-bottom: 20px"
@mouseover.native="hover($event)"
@mouseout.native="removeClass($event)"
>
<el-checkbox
:label="item.index"
>【{{ item.index }}】、{{ item.content }}</el-checkbox>
</el-col>
</el-checkbox-group>
</el-form-item>
</template>
</el-form>
这里仅需要处理输入事件即可,我们返回一个数组作为多选题的答案。
handleInput(value) {
var rtn = {
...this.answer,
content: [],
state: value.length > 0
}
var arr = value || []
arr.forEach(item => {
if (item != '' && item != '-1') {
rtn.content.push(item)
}
})
console.log('multi handleInput:', rtn)
this.$emit('input', rtn)
}
2.4 图片题
图片题是单选题的变种,只是标题是图片而已,因此我们只需要复制一个单选题,修改标题。
<el-image style="width: 381px; height: 411px" :src="question.title" lazy />
哈哈,搞定了。
3. 构建父组件
题型组件已经构建完成了,那我们需要一个父组件封装适合各种题型的组件。
这个组合采用题型进行判断:
<template>
<div>
<template v-if="question.type=='single'">
<single-item
v-model="answerVal"
:question="question"
@input="handleInput"
/>
</template>
<template v-else-if="question.type=='multi'">
<multi-item
v-model="answerVal"
:question="question"
@input="handleInput"
/>
</template>
<template v-else-if="question.type=='image'">
<image-single-item
v-model="answerVal"
:question="question"
@input="handleInput"
/>
</template>
<template v-else-if="question.type=='text'">
<text-item
v-model="answerVal"
:question="question"
@input="handleInput"
/>
</template>
<template v-else>
dont setup question type
</template>
</div>
</template>
实现代码如下:
export default {
name: 'WQuestionItem',
components: { SingleItem, MultiItem, TextItem, ImageSingleItem },
model: {
prop: 'answer',
event: 'input'
},
props: {
question: {
type: Object,
required: true,
default: () => {
return {}
}
},
answer: {
type: Object
},
},
data() {
return {
answerVal: JSON.parse(JSON.stringify(this.answer))
}
},
computed: {
},
methods: {
handleInput(val) {
console.log('wquestionItem', val)
this.$emit('input', val)
}
}
}
运行下效果~~~~
4 小结
利用vue 的组件,我们实现了单选、多选、填空、图片等题型的设计,如果你还有更多的需求,可以在此基础上方便的扩展。
关注我,不迷路;一块学习vue,一块进步!