197 lines
6.6 KiB
Vue
197 lines
6.6 KiB
Vue
<template>
|
||
<view class="h-full flex flex-col">
|
||
<view class="flex-shrink-0 px-10 pt-5 text-lg text-primary font-semibold text-center truncate">{{ survey.name }}
|
||
</view>
|
||
<view v-if="survey.description"
|
||
class="flex-shrink-0 px-10 mt-10 text-sm leading-normal text-gray line-clamp-2 text-center">
|
||
{{ survey.description }}
|
||
</view>
|
||
<view v-if="survey.enableScore" class="text-right mt-10 px-10 text-sm">
|
||
当前得分:<span class="text-primary text-base font-semibold min-w-5 inline-block">{{ allScore }} </span>
|
||
</view>
|
||
<view v-if="quesiton" class="my-3 text-base font-semibold px-10 leading-normal ">
|
||
<span v-if="quesiton.require" class="text-xs text-danger">*</span> {{ index + 1 }}、{{ quesiton.title }}
|
||
</view>
|
||
<view v-if="quesiton" class="flex-grow relative">
|
||
<scroll-view :scroll-y="true" class="absolute inset-0">
|
||
<view class="px-10">
|
||
<template v-if="quesiton.type === 'radio'">
|
||
<view v-for="(opt, idx) in quesiton.options" :key="opt.value" class="flex py-4 border-b "
|
||
:class="idx === 0 ? 'border-t' : ''" @click="changeRadio(opt.value)">
|
||
<uni-icons v-if="answer[quesiton.id] === opt.value" class="mr-2 text-primary flex-shrink-0" color=" "
|
||
type="checkbox-filled" size="24"></uni-icons>
|
||
<uni-icons v-else class="mr-2 text-gray flex-shrink-0" color=" " type="circle" size="24"></uni-icons>
|
||
<view class="flex-grow text-base leading-normal">
|
||
{{ opt.label }}
|
||
<span v-if="survey.enableScore && opt.score >= 0">
|
||
( {{ opt.score }}分)
|
||
</span>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<textarea v-else-if="quesiton.type === 'input'" :value="answer[quesiton.id]" placeholder="请输入..."
|
||
placeholder-class="text-gray text-sm"
|
||
class="p-10 text-sm w-full box-border rounded border min-h-30 border-gray-200" @input="change($event)" />
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
<view v-if="list.length > 1" class="flex-shrink-0 py-3">
|
||
<view class="mx-3 bg-gray-100 h-3 rounded-lg overflow-hidden">
|
||
<view class="h-3 rounded-lg bg-primary" :style="{ width: `${progress}%` }"></view>
|
||
</view>
|
||
<view class="flex items-center justify-between px-10 py-15">
|
||
<view class="flex items-center text-primary" :class="index > 0 ? '' : 'opacity-0'" @click="prev()">
|
||
<uni-icons class="inline-block mr-10rpx" color=" " type="arrowleft"></uni-icons>
|
||
<span class="text-base font-semibold">上一题</span>
|
||
</view>
|
||
<!-- 当前得分:40 -->
|
||
<view class="text-sm">{{ index + 1 }} / {{ list.length }}</view>
|
||
<view v-if="index < list.length - 1" class="flex items-center text-primary"
|
||
:class="index < list.length - 1 ? '' : 'opacity-0'" @click="next()">
|
||
<span class="text-base font-semibold">下一题</span>
|
||
<uni-icons class="inline-block ml-10rpx" color=" " type="arrowright"></uni-icons>
|
||
</view>
|
||
<view v-if="index === list.length - 1" class="flex items-center text-primary" @click="submit()">
|
||
<span class="text-base font-semibold">提交</span>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-if="list.length === 1"
|
||
class="flex-shrink-0 mx-10 py-12 leading-normal text-white text-center rounded bg-primary" @click="submit()">
|
||
提交
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
<script>
|
||
import { toast } from '@/utils/widget'
|
||
export default {
|
||
name: 'Question',
|
||
props: {
|
||
list: { type: Array, default: () => ([]) },
|
||
survey: { type: Object, default: () => ({}) }
|
||
},
|
||
data() {
|
||
return {
|
||
index: 0,
|
||
answer: {},
|
||
waiting: false
|
||
}
|
||
},
|
||
computed: {
|
||
quesiton() {
|
||
return this.list[this.index]
|
||
},
|
||
progress() {
|
||
return (this.index + 1) / (this.list.length || 1) * 100
|
||
},
|
||
allScore() {
|
||
return this.list.reduce((score, item) => {
|
||
if (item.type === 'radio') {
|
||
const opt = item.options.find(i => i.value && i.value === this.answer[item.id])
|
||
if (opt && opt.score >= 0) {
|
||
score = Math.floor(score * 100 + opt.score * 100) / 100
|
||
}
|
||
}
|
||
return score
|
||
}, 0)
|
||
}
|
||
},
|
||
methods: {
|
||
|
||
change(e) {
|
||
this.$set(this.answer, this.quesiton.id, e.detail.value)
|
||
},
|
||
changeIndex(num) {
|
||
if (this.waiting) return;
|
||
this.waiting = true;
|
||
setTimeout(() => this.waiting = false, 1000);
|
||
const target = num + this.index;
|
||
this.index = target >= 0 && target < this.list.length ? target : this.index
|
||
},
|
||
changeRadio(value) {
|
||
this.$set(this.answer, this.quesiton.id, value)
|
||
},
|
||
prev() {
|
||
this.changeIndex(-1)
|
||
},
|
||
next() {
|
||
if (this.quesiton.require && this.quesiton.type === 'radio' && !this.quesiton.options.some(i => i.value && i.value === this.answer[this.quesiton.id])) {
|
||
toast('请完成当前题目')
|
||
} else if (this.quesiton.require && this.quesiton.type === 'input' && (typeof this.answer[this.quesiton.id] !== 'string' || this.answer[this.quesiton.id].trim() === '')) {
|
||
toast('请完成当前题目')
|
||
} else {
|
||
this.changeIndex(1)
|
||
}
|
||
},
|
||
verify() {
|
||
const index = this.list.findIndex(i => {
|
||
if (i.require && i.type === 'radio' && !i.options.some(j => j.value && j.value === this.answer[i.id])) {
|
||
return true
|
||
}
|
||
if (i.require && i.type === 'input' && (typeof this.answer[i.id] !== 'string' || this.answer[i.id].trim() === '')) {
|
||
return true
|
||
}
|
||
return false
|
||
})
|
||
if (index >= 0) {
|
||
toast('请完成第' + (index + 1) + '题')
|
||
this.index = index
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
submit() {
|
||
if (this.verify()) {
|
||
const list = this.list.map(i => {
|
||
const item = {
|
||
id: i.id,
|
||
value: this.answer[i.id] || '',
|
||
title: i.title,
|
||
type: i.type,
|
||
require: i.require,
|
||
}
|
||
if (Array.isArray(i.options)) {
|
||
item.options = i.options.map(opt => ({ ...opt }))
|
||
}
|
||
return item
|
||
})
|
||
this.$emit('submit', { list, score: this.allScore })
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
.pt-5 {
|
||
padding-top: 40rpx;
|
||
}
|
||
|
||
.min-w-5 {
|
||
min-width: 40rpx;
|
||
}
|
||
|
||
.inline-block {
|
||
display: inline-block;
|
||
}
|
||
|
||
.my-3 {
|
||
margin-top: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.py-4 {
|
||
padding-top: 30rpx;
|
||
padding-bottom: 30rpx;
|
||
}
|
||
|
||
.border-t {
|
||
border-top: 1px solid #eee;
|
||
}
|
||
|
||
.min-h-30 {
|
||
min-height: 200rpx;
|
||
}
|
||
</style>
|