2026-02-28 11:18:20 +08:00
|
|
|
|
<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"
|
2026-03-05 17:04:49 +08:00
|
|
|
|
class="flex-shrink-0 mx-10 py-12 leading-normal text-white text-center rounded bg-primary" @click="submit()">
|
2026-02-28 11:18:20 +08:00
|
|
|
|
提交
|
|
|
|
|
|
</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>
|