| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- <template>
- <view class="question-item" :id="`qs_${question.id}`">
- <view class="is-main-question">
- <view v-if="question.typeId && !isSubQuestion" class="question-type">
- {{ questionTypeDesc[question.typeId as EnumQuestionType] }}
- </view>
- <view class="question-content" :class="{ 'mt-30': isSubQuestion }">
- <text v-if="question.isSubQuestion" class="text-nowrap text-30">{{ getQuestionTitle() }} </text>
- <uv-parse :content="question.title" containerStyle="display:inline"
- contentStyle="word-break:break-word;"></uv-parse>
- </view>
- <view class="question-options">
- <view class="question-option" v-for="option in question.options" :class="getStyleClass(option)" :key="option.id"
- @click="handleSelect(option)">
- <template v-if="!readonly">
- <view v-if="!isOnlySubjective" class="question-option-index">{{ option.no }}</view>
- <view v-else>
- <uv-icon name="info-circle" :color="isSelected(option) ? '#31A0FC' : '#999'" size="18" />
- </view>
- </template>
- <view v-else>
- <uv-icon v-if="isOptionCorrect(question, option)" name="checkmark-circle-fill" color="#2CC6A0" size="22" />
- <uv-icon v-else-if="isOptionIncorrect(option)" name="close-circle-fill" color="#FF5B5C" size="22" />
- <view v-else class="question-option-index">{{ option.no }}</view>
- </view>
- <view class="question-option-content">
- <uv-parse :content="getOptionContent(option)" containerStyle="display:inline"
- contentStyle="word-break:break-word;"></uv-parse>
- </view>
- </view>
- <view v-if="question.options.length && !readonly && !isOnlySubjective" class="question-option"
- :class="{ 'question-option-not-know': question.isNotKnow }" @click="handleNotKnow">
- <view class="question-option-index">
- <uv-icon name="info-circle" :color="question.isNotKnow ? '#31A0FC' : '#999'" size="18" />
- </view>
- <view class="question-option-content text-fore-light">不会</view>
- </view>
- <view v-if="!readonly && isOnlySubjective" class="mt-40 bg-[#EBF9FF] p-12 rounded-8">
- <view class="rounded-8 bg-white px-10 py-20 text-primary text-24 flex gap-x-6 items-center">
- <uv-icon name="info-circle" color="#31A0FC" size="16" />
- <text>请线下答题,查看解析对比后,选“会”或“不会”</text>
- </view>
- <view class="mt-30 mb-20 text-24 text-white bg-primary w-fit mx-auto px-20 py-12 rounded-full text-center"
- @click="handleShowParse">
- 查看解析</view>
- </view>
- </view>
- <!-- 阅卷模式下显示答案 -->
- <template v-if="readonly">
- <view v-if="question.subQuestions.length === 0" class="answer-wrap mt-40 rounded-8 pt-60 pb-40 flex items-center text-center relative">
- <ie-image v-if="question.isCorrect" src="/pagesStudy/static/image/icon-answer-correct.png"
- class="absolute top-0 left-1/2 -translate-x-1/2 w-222 h-64" />
- <ie-image v-else src="/pagesStudy/static/image/icon-answer-incorrect.png"
- class="absolute top-0 left-1/2 -translate-x-1/2 w-240 h-64" />
- <view v-if="!isOnlySubjective" class="flex-1">
- <view class="text-34 text-[#2CC6A0] font-bold">{{ question.answer1 }}</view>
- <view class="mt-4 text-26">正确答案</view>
- </view>
- <view class="h-40 w-1 bg-back"></view>
- <view v-if="!isOnlySubjective" class="flex-1">
- <view class="text-34 font-bold" :class="[question.isCorrect ? 'text-[#2CC6A0]' : 'text-[#FF5B5C]']">
- {{ question.answers.join('') || (question.isNotKnow ? '不会' : '未作答') }}
- </view>
- <view class="mt-4 text-26">我的答案</view>
- </view>
- <view v-if="isOnlySubjective" class="text-left mt-10 px-20">
- <uv-parse :content="'参考答案:' + question.answer2"></uv-parse>
- </view>
- </view>
- </template>
- <view v-if="(readonly && question.parse) || (!readonly && question.showParse)" class="mt-40">
- <view class="text-30 text-fore-title font-bold">解析</view>
- <view class="mt-10 text-26 text-fore-light">
- <uv-parse :content="question.parse || '暂无解析'"></uv-parse>
- </view>
- </view>
- </view>
- <view v-if="question.subQuestions.length" class="is-sub-question">
- <scroll-view class="w-full h-fit sticky top-0 bg-white py-10 z-1" scroll-x>
- <view class="flex items-center px-20 gap-x-20">
- <view class="px-40 py-8 rounded-full"
- :class="[subIndex === subQuestionIndex ? 'bg-[#EBF9FF] text-primary font-bold' : 'bg-back']"
- v-for="(subQuestion, subIndex) in question.subQuestions" @click="changeSubQuestion(subIndex)">
- {{ question.index + question.offset + subIndex + 1 }}
- </view>
- </view>
- </scroll-view>
- <view v-if="subQuestion">
- <question-item :question="subQuestion" :parent-question="question" :readonly="readonly" :is-sub-question="true"
- :index="index" :total="question.subQuestions.length" @select="handleSelectOption"
- @notKnow="handleSelectNotKnow" />
- </view>
- </view>
- </view>
- </template>
- <script lang="ts" setup>
- import { Study } from '@/types';
- import { useExam } from '@/composables/useExam';
- import { EnumQuestionType } from '@/common/enum';
- import { NEXT_QUESTION, PREV_QUESTION, NEXT_QUESTION_QUICKLY, PREV_QUESTION_QUICKLY, SHOW_SUBMIT_CONFIRM, IS_ALL_DONE } from '@/types/injectionSymbols';
- const { questionTypeDesc, isOptionCorrect } = useExam();
- const props = defineProps<{
- question: Study.Question;
- parentQuestion?: Study.Question;
- readonly?: boolean;
- isSubQuestion?: boolean;
- index: number;
- total?: number;
- subQuestionIndex?: number;
- }>();
- const nextQuestion = inject(NEXT_QUESTION);
- const prevQuestion = inject(PREV_QUESTION);
- const nextQuestionQuickly = inject(NEXT_QUESTION_QUICKLY);
- const prevQuestionQuickly = inject(PREV_QUESTION_QUICKLY);
- const isAllDone = inject(IS_ALL_DONE);
- const subQuestion = computed(() => {
- return props.question.subQuestions[props.subQuestionIndex ?? 0];
- });
- const emit = defineEmits<{
- (e: 'update:question', question: Study.Question): void;
- (e: 'select', question: Study.Question): void;
- (e: 'notKnow', question: Study.Question): void;
- (e: 'scrollTo', selector: string): void;
- (e: 'changeSubQuestion', index: number): void;
- (e: 'selectSubQuestion', index: number): void;
- (e: 'changeQuestion', question: Study.Question): void;
- }>();
- const isOnlySubjective = computed(() => {
- // 除了单选、判断、多选题,其他题型都是主观题
- return ![EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT, EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId);
- });
- const getStyleClass = (option: Study.QuestionOption) => {
- if (!props.readonly) {
- return isSelected(option) ? 'question-option-selected' : '';
- }
- let customClass = '';
- let { answers, answer1 } = props.question;
- answers = answers?.filter(item => item !== ' ') || [];
- answer1 = answer1 || ''
- if ([EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT].includes(props.question.typeId)) {
- if (answer1.includes(option.no)) {
- customClass = 'question-option-correct';
- } else if (answers.includes(option.no)) {
- customClass = 'question-option-incorrect';
- }
- } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId)) {
- // 我选择的答案
- if (answers.includes(option.no)) {
- if (answer1.includes(option.no)) {
- customClass = 'question-option-correct';
- } else {
- customClass = 'question-option-incorrect';
- }
- } else {
- // 漏选的答案
- if (answer1.includes(option.no)) {
- customClass = 'question-option-miss';
- }
- }
- }
- // console.log(props.question, option)
- return customClass;
- };
- const isOptionIncorrect = (option: Study.QuestionOption) => {
- const { answers, answer1 } = props.question;
- return answers.includes(option.no) && !answer1.includes(option.no);
- }
- const getQuestionTitle = () => {
- if (props.isSubQuestion) {
- const prefix = questionTypeDesc[props.question.typeId as EnumQuestionType].slice(0, 2);
- return `[${prefix}]`;
- }
- return '';
- };
- const getOptionContent = (option: Study.QuestionOption) => {
- // sb 问题,浪费几个小时
- return option.name.replace(/\s/g, ' ');
- }
- const handleShowParse = () => {
- props.question.showParse = !props.question.showParse;
- }
- const handleNotKnow = () => {
- props.question.answers = [];
- props.question.isNotKnow = !props.question.isNotKnow;
- checkIsDone();
- if (props.isSubQuestion) {
- emit('select', props.question);
- } else {
- changeQuestion();
- }
- }
- const handleSelect = (option: Study.QuestionOption) => {
- if (props.readonly) {
- return;
- }
- if ([
- EnumQuestionType.JUDGMENT,
- EnumQuestionType.SINGLE_CHOICE,
- EnumQuestionType.SUBJECTIVE,
- EnumQuestionType.SHORT_ANSWER,
- EnumQuestionType.ESSAY,
- EnumQuestionType.ANALYSIS
- ].includes(props.question.typeId)) {
- props.question.answers = [option.no];
- // nextQuestion?.();
- } else if (props.question.typeId === EnumQuestionType.MULTIPLE_CHOICE) {
- if (props.question.answers.includes(option.no)) {
- props.question.answers = props.question.answers.filter(item => item !== option.no);
- } else {
- props.question.answers.push(option.no);
- }
- }
- props.question.isNotKnow = false;
- checkIsDone(props.question);
- if (props.question.isSubQuestion) {
- // 同时检查父题是否已完成
- checkIsDone(props.parentQuestion);
- }
- if (props.isSubQuestion) {
- emit('select', props.question);
- } else {
- changeQuestion()
- }
- }
- const checkIsDone = (question?: Study.Question) => {
- if (!question) {
- return;
- }
- if (question?.subQuestions && question?.subQuestions.length > 0) {
- question.isDone = question.subQuestions.every(q => q.answers.length > 0 || q.isNotKnow);
- } else {
- question.isDone = question.answers.length > 0 || question.isNotKnow;
- }
- }
- // 子题选中方法
- const handleSelectOption = () => {
- findNotDoneSubQuestion();
- }
- const handleSelectNotKnow = () => {
- findNotDoneSubQuestion();
- }
- // 查找是否有子题没有做,有的话就自动滚动到目标处
- const findNotDoneSubQuestion = () => {
- console.log(props)
- if (props.question.subQuestions.length - 1 === props.subQuestionIndex) {
- changeQuestion();
- } else {
- changeSubQuestion((props.subQuestionIndex ?? 0) + 1);
- }
- // const notDoneSubQuestion = props.question.subQuestions.find(q => !q.isDone);
- // if (notDoneSubQuestion) {
- // if (notDoneSubQuestion.subIndex !== undefined) {
- // changeSubQuestion(notDoneSubQuestion.subIndex);
- // }
- // } else {
- // // 是否当前是最后一个子题
- // if (props.question.subQuestions.length - 1 === props.subQuestionIndex) {
- // changeQuestion();
- // } else {
- // changeSubQuestion(props.subQuestionIndex ?? 0 + 1);
- // }
- // }
- }
- const changeSubQuestion = (index: number) => {
- emit('changeSubQuestion', index);
- }
- const changeQuestion = () => {
- emit('changeQuestion', props.question);
- }
- const isSelected = (option: Study.QuestionOption) => {
- const { typeId, answers } = props.question;
- if ([
- EnumQuestionType.JUDGMENT,
- EnumQuestionType.SINGLE_CHOICE,
- EnumQuestionType.SUBJECTIVE,
- EnumQuestionType.SHORT_ANSWER,
- EnumQuestionType.ESSAY,
- EnumQuestionType.ANALYSIS
- ].includes(typeId)) {
- return answers.includes(option.no);
- } else if (typeId === EnumQuestionType.MULTIPLE_CHOICE) {
- return answers.includes(option.no);
- }
- return false;
- }
- </script>
- <style lang="scss" scoped>
- .answer-wrap {
- box-shadow: 0 0 10px 0px rgba(0, 0, 0, 0.06);
- }
- .prefix {
- @apply relative pl-20 before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:w-[3px] before:h-[15px] before:bg-primary before:rounded-full;
- }
- .is-sub-question {
- @apply px-0;
- .question-options {
- @apply mt-0;
- }
- }
- .is-main-question {
- @apply px-40;
- >.question-options {
- @apply mt-40;
- }
- }
- .question-item {
- .question-type {
- @apply mb-20 text-32 text-fore-subtitle font-bold;
- }
- .sub-question-type {
- @apply text-28 text-fore-light font-bold;
- }
- .question-content {
- @apply text-32 text-fore-title break-words;
- }
- .question-options {
- .question-option {
- @apply flex items-center px-30 py-24 bg-back rounded-8 border border-none border-transparent;
- .question-option-index {
- @apply w-40 h-40 rounded-full bg-transparent text-30 text-fore-light font-bold flex items-center justify-center flex-shrink-0;
- }
- .question-option-content {
- @apply text-28 text-fore-title ml-20 flex-1 min-w-0;
- }
- }
- .question-option-selected {
- @apply bg-[#b5eaff8e];
- .question-option-index {
- @apply bg-primary text-white;
- }
- .question-option-content {
- @apply text-primary;
- }
- }
- .question-option-not-know {
- @apply bg-[#b5eaff8e];
- .question-option-content {
- @apply text-primary;
- }
- }
- .question-option-correct,
- .question-option-miss {
- @apply bg-[#E7FCF8] border-[#E7FCF8] text-[#2CC6A0];
- .question-option-index {
- @apply text-[#2CC6A0];
- }
- .question-option-content {
- @apply text-[#2CC6A0];
- }
- }
- .question-option-miss {
- @apply relative overflow-hidden;
- &::before {
- content: '';
- position: absolute;
- right: -56rpx;
- top: 15rpx;
- width: 180rpx;
- height: 36rpx;
- background: rgba(255, 91, 92, 0.2);
- transform: rotate(30deg);
- box-shadow: 0 2rpx 4rpx rgba(255, 91, 92, 0.1);
- }
- &::after {
- content: '漏选';
- position: absolute;
- right: -8rpx;
- top: 14rpx;
- width: 100rpx;
- height: 32rpx;
- color: #FF5B5C;
- font-size: 20rpx;
- // font-weight: bold;
- transform: rotate(30deg);
- display: flex;
- align-items: center;
- justify-content: center;
- line-height: 1;
- }
- }
- .question-option-incorrect {
- @apply bg-[#FEEDE9] border-[#FEEDE9] text-[#FF5B5C];
- .question-option-index {
- @apply text-[#FF5B5C];
- }
- .question-option-content {
- @apply text-[#FF5B5C];
- }
- }
- .question-option+.question-option {
- @apply mt-24;
- }
- }
- }
- </style>
|