猫妈狗爸伴宠师小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

346 lines
8.3 KiB

<template>
<view class="mt32 question__view" :class="[props.mode]">
<view class="size-28 mb20 question">
{{ props.data.title }}
<text class="question-type" v-if="props.data.typeAnswer == 0">
{{ isMultipleChoice ? '(多选题)' : '(单选题)' }}
</text>
<text class="question-type" v-else-if="props.data.typeAnswer == 1">
(填空题)
</text>
<!-- {{ `${props.index + 1}、${props.data.title}` }} -->
</view>
<template v-if="props.mode === 'edit'">
<template v-if="props.data.typeAnswer == 0">
<view class="size-28 option" v-for="(option, oIdx) in props.data.options"
:key="`${props.index}-option-${oIdx}`"
:class="[isOptionSelected(option.id) ? 'is-selected' : '']"
@click="onChange(option.id)">
{{ option.title }}
</view>
</template>
<template v-else>
<view class="textarea">
<textarea v-model="value" :placeholder="`请输入您的答案,不得低于${props.data.numberWords || 0}个字`" :rows="10"
@blur="onChange($event.detail.value)" :maxlength="2000"></textarea>
</view>
</template>
</template>
<template v-else>
<template v-if="props.data.typeAnswer == 0">
<view class="size-28 option" v-for="(option, oIdx) in props.data.options"
:key="`${props.index}-option-${oIdx}`" :class="[
option.isTrue ? 'is-correct' : '',
isDisplayOptionSelected(option.id) && !option.isTrue ? 'is-error' : '',
isDisplayOptionSelected(option.id) && option.isTrue ? 'is-correct-selected' : ''
]">
{{ option.title }}
<!-- 正确答案标识 -->
<view class="icon icon-correct" v-if="option.isTrue">
<up-icon name="checkmark" color="#05C160" size="35rpx"></up-icon>
</view>
<!-- 错误选择标识 -->
<view class="icon icon-error" v-if="isDisplayOptionSelected(option.id) && !option.isTrue">
<up-icon name="close" color="#FF2A2A" size="35rpx"></up-icon>
</view>
<!-- 用户选择标识和正确答案标识 -->
<view class="answer-tags" v-if="isDisplayOptionSelected(option.id) || option.isTrue">
<view class="user-choice-tag" v-if="isDisplayOptionSelected(option.id)">
我的选择
</view>
<view class="correct-answer-tag" v-if="option.isTrue">
正确答案
</view>
</view>
</view>
</template>
<template v-else>
<view class="textarea">
<view class="user-answer-label">我的答案:</view>
<view class="user-answer">{{ props.data.value || '未作答' }}</view>
<view class="correct-answer-label" v-if="props.data.answer">正确答案:</view>
<view class="highlight" v-if="props.data.answer">{{ props.data.answer }}</view>
</view>
</template>
</template>
</view>
</template>
<script setup>
import {
computed,
watch
} from 'vue'
import {
addBaseAnswer,
addTrainAnswer
} from '@/api/examination'
import {
store
} from '@/store'
const userId = computed(() => {
return store.state.user.userInfo.userId
})
const props = defineProps({
index: {
type: Number,
default: null,
},
data: {
type: Object,
default () {
return {}
}
},
modelValue: {
type: [String, Number, Array],
default: null,
},
mode: {
type: String,
default: 'edit', // edit | display
},
type: {
type: String,
default: null, // '基本' | '培训'
}
})
const min = 700
const emit = defineEmits(['update:modelValue'])
// 判断是否是多选题
const isMultipleChoice = computed(() => {
if (!props.data.options || !props.data.options.length) return false
const correctAnswers = props.data.options.filter(option => option.isTrue === 1 || option.isTrue === true)
return correctAnswers.length > 1
})
const value = computed({
set(val) {
emit('update:modelValue', val)
},
get() {
if (isMultipleChoice.value) {
// 多选题,确保返回数组
return Array.isArray(props.modelValue) ? props.modelValue : (props.modelValue ? [props.modelValue] : [])
} else {
// 单选题,返回单个值
return Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue
}
}
})
// 判断选项是否被选中
const isOptionSelected = (optionId) => {
if (isMultipleChoice.value) {
return Array.isArray(value.value) && value.value.includes(optionId)
} else {
return value.value === optionId
}
}
// 显示模式下判断选项是否被用户选中
const isDisplayOptionSelected = (optionId) => {
if (props.mode !== 'display') return false
// 方法1: 从 props.data.value 获取用户答案(直接传入的答案)
if (props.data.value !== undefined && props.data.value !== null) {
if (Array.isArray(props.data.value)) {
return props.data.value.includes(optionId)
} else {
return props.data.value === optionId
}
}
// 方法2: 从 userAnswerBaseList 获取用户答案(从后端获取的答案数据)
if (props.data.userAnswerBaseList && props.data.userAnswerBaseList.length > 0) {
return props.data.userAnswerBaseList.some(answer => answer.answerId === optionId)
}
// 方法3: 从 modelValue 获取答案(当前组件的值)
if (props.modelValue !== undefined && props.modelValue !== null) {
if (Array.isArray(props.modelValue)) {
return props.modelValue.includes(optionId)
} else {
return props.modelValue === optionId
}
}
return false
}
const onChange = (val) => {
if (props.data.typeAnswer == 1) {
// 填空题处理
value.value = val
} else if (props.data.typeAnswer == 0) {
// 选择题处理
if (isMultipleChoice.value) {
// 多选题处理
let currentValue = Array.isArray(value.value) ? [...value.value] : []
const index = currentValue.indexOf(val)
if (index > -1) {
// 已选中,取消选择
currentValue.splice(index, 1)
} else {
// 未选中,添加选择
currentValue.push(val)
}
value.value = currentValue
} else {
// 单选题处理
value.value = val
}
}
// const data = {
// userId: userId.value,
// questionId: props.data.id,
// }
// if (props.type === '基本') {
// data.answerId = val
// addBaseAnswer(data)
// } else if (props.type === '培训') {
// data.answer = val
// addTrainAnswer(data)
// }
}
</script>
<style lang="scss" scoped>
.question {
color: #000000;
.question-type {
color: #FFBF60;
font-size: 22rpx;
margin-left: 10rpx;
}
}
.option {
background-color: #F3F3F3;
color: #707070;
line-height: 37rpx;
padding: 23rpx;
border-radius: 28rpx;
position: relative;
&+& {
margin-top: 20rpx;
}
.icon {
position: absolute;
right: 45rpx;
bottom: 23rpx;
display: none;
}
}
.textarea {
background-color: #F3F3F3;
padding: 23rpx;
border-radius: 16rpx;
.highlight {
color: #FF2A2A;
font-size: 28rpx;
}
}
.question__view.edit {
.option.is-selected {
background-color: rgba($color: #FFBF60, $alpha: 0.22);
color: #FFBF60;
}
}
.question__view.display {
.option {
&.is-correct {
background-color: rgba($color: #05C160, $alpha: 0.08);
color: #05C160;
.icon-correct {
display: block;
}
}
&.is-error {
background-color: rgba($color: #FFEBCE, $alpha: 0.36);
color: #FF2A2A;
.icon-error {
display: block;
}
}
&.is-correct-selected {
background-color: rgba($color: #05C160, $alpha: 0.15);
color: #05C160;
border: 2px solid #05C160;
}
}
.answer-tags {
margin-top: 10rpx;
display: flex;
gap: 10rpx;
flex-wrap: wrap;
}
.user-choice-tag {
background-color: #FFBF60;
color: white;
font-size: 20rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
display: inline-block;
}
.correct-answer-tag {
background-color: #05C160;
color: white;
font-size: 20rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
display: inline-block;
}
}
.textarea {
.user-answer-label, .correct-answer-label {
font-size: 24rpx;
color: #999;
margin-bottom: 10rpx;
}
.user-answer {
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
padding: 10rpx;
background-color: rgba($color: #FFBF60, $alpha: 0.1);
border-radius: 8rpx;
}
.highlight {
color: #05C160;
font-size: 28rpx;
font-weight: bold;
padding: 10rpx;
background-color: rgba($color: #05C160, $alpha: 0.1);
border-radius: 8rpx;
}
}
</style>