|
|
- <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>
|