|
@@ -1,105 +1,109 @@
|
|
|
+view
|
|
|
<template>
|
|
|
- <view class="topic-container">
|
|
|
- <scroll-view
|
|
|
- scroll-x
|
|
|
- :scroll-into-view="`item-${nowIndex}`"
|
|
|
- class="topic-scroll"
|
|
|
- :scroll-with-animation="true"
|
|
|
- :scroll-anchoring="true"
|
|
|
- :show-scrollbar="false"
|
|
|
- >
|
|
|
- <template v-for="(item, parindex) in topics.ques" :key="parindex">
|
|
|
- <view
|
|
|
- :id="`item-${parindex}`"
|
|
|
- class="topic-item"
|
|
|
- :style="{
|
|
|
- width: `${windowWidth}px`,
|
|
|
- height: `${windowHeight}px`,
|
|
|
- flexShrink: 0,
|
|
|
- }"
|
|
|
- >
|
|
|
- <!-- 头部 -->
|
|
|
- <view class="topic-header">
|
|
|
- <view class="topic-header-left">
|
|
|
- <view class="topic-type">
|
|
|
- {{ answerList[parindex]?.length > 1 ? "多选题" : "单选题" }}
|
|
|
- </view>
|
|
|
- <view class="topic-count">
|
|
|
- 第{{ parindex + 1 }}题/共{{ topics.ques.length }}题
|
|
|
- </view>
|
|
|
+ <Container
|
|
|
+ :scrollX="true"
|
|
|
+ :scrollY="false"
|
|
|
+ :scroll-into-view="`item-${nowIndex}`"
|
|
|
+ @onSafeAreaChange="onSafeAreaChange"
|
|
|
+ >
|
|
|
+ <template v-for="(item, parindex) in topics.ques" :key="parindex">
|
|
|
+ <view
|
|
|
+ :id="`item-${parindex}`"
|
|
|
+ class="topic-item"
|
|
|
+ :style="{
|
|
|
+ width: `${safeArea.width}px`,
|
|
|
+ height: `${safeArea.height}px`,
|
|
|
+ flexShrink: 0, // 解决宽度无效问题
|
|
|
+ marginLeft: parindex !== 0 ? `${24}rpx` : `0`
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <!-- 头部 -->
|
|
|
+ <view class="topic-header">
|
|
|
+ <view class="topic-header-left">
|
|
|
+ <view class="topic-type">
|
|
|
+ {{
|
|
|
+ item.mode || (item.anslist?.length > 1 ? "多选题" : "单选题")
|
|
|
+ }}
|
|
|
</view>
|
|
|
- <view class="star-icon" @tap="handleStar(item)">
|
|
|
- <text
|
|
|
- class="iconfont"
|
|
|
- :class="starIcon === 'star' ? 'icon-star' : 'icon-star-filled'"
|
|
|
- ></text>
|
|
|
+ <view class="topic-count">
|
|
|
+ 第{{ parindex + 1 }}题/共{{ topics.ques.length }}题
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <!-- 问题内容 -->
|
|
|
- <view class="topic-content">
|
|
|
- <view class="question-text">{{ item.title }}</view>
|
|
|
- <questions
|
|
|
- v-for="(question, index) in item.questions"
|
|
|
- :key="index"
|
|
|
- :answer-list="
|
|
|
- Array.isArray(item.anslist) ? item.anslist : [item.anslist]
|
|
|
- "
|
|
|
- :index="index"
|
|
|
- :select-count="selectIndexList[parindex] || []"
|
|
|
- :question="question"
|
|
|
- :parindex="parindex"
|
|
|
- @select="handleSelect"
|
|
|
- @show-answer="(index) => setShowAnswer(index, parindex)"
|
|
|
- />
|
|
|
+ <view class="star-icon" @tap="handleStar(item)">
|
|
|
+ <text
|
|
|
+ class="iconfont"
|
|
|
+ :class="starIcon === 'star' ? 'icon-star' : 'icon-star-filled'"
|
|
|
+ ></text>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 问题内容 -->
|
|
|
+ <view class="topic-content">
|
|
|
+ <view class="question-text">{{ item.title }}</view>
|
|
|
+ <questions
|
|
|
+ v-for="(question, index) in item.questions"
|
|
|
+ :key="index"
|
|
|
+ :answer-list="
|
|
|
+ Array.isArray(item.anslist) ? item.anslist : [item.anslist]
|
|
|
+ "
|
|
|
+ :index="index"
|
|
|
+ :select-count="selectIndexList[parindex] || []"
|
|
|
+ :question="question"
|
|
|
+ :parindex="parindex"
|
|
|
+ :mode="mode"
|
|
|
+ @select="handleSelect"
|
|
|
+ @show-answer="(index) => setShowAnswer(index, parindex)"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 答案展示 -->
|
|
|
- <view v-if="showAnswer[parindex]" class="answer-section">
|
|
|
- <view class="answer-content">
|
|
|
- <view class="answer-row">
|
|
|
- <view class="answer-item">
|
|
|
- 正确答案:
|
|
|
- <text class="answer-text">{{
|
|
|
- getRightAnswer(parindex)
|
|
|
- }}</text>
|
|
|
- </view>
|
|
|
- <view class="answer-item">
|
|
|
- 我的答案:
|
|
|
- <text class="answer-text">{{ getMyAnswer(parindex) }}</text>
|
|
|
- </view>
|
|
|
+ <!-- 答案展示 -->
|
|
|
+ <view
|
|
|
+ v-if="showAnswer[parindex] && mode === 'practice'"
|
|
|
+ class="answer-section"
|
|
|
+ >
|
|
|
+ <view class="answer-content">
|
|
|
+ <view class="answer-row">
|
|
|
+ <view class="answer-item border-r-primary">
|
|
|
+ 正确答案:
|
|
|
+ <text class="answer-text">{{ getRightAnswer(parindex) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="answer-item">
|
|
|
+ 我的答案:
|
|
|
+ <text class="answer-text">{{ getMyAnswer(parindex) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
|
|
|
- <!-- 底部按钮 -->
|
|
|
- <view class="button-group">
|
|
|
- <button
|
|
|
- v-if="parindex >= 1 && parindex < topics.ques.length - 1"
|
|
|
- class="prev-btn"
|
|
|
- @tap="handlePrevPage(item, parindex)"
|
|
|
- >
|
|
|
- 上一题
|
|
|
- </button>
|
|
|
- <button
|
|
|
- v-if="parindex < topics.ques.length - 1"
|
|
|
- class="next-btn"
|
|
|
- @tap="handleNextPage(item, parindex)"
|
|
|
- >
|
|
|
- 下一题
|
|
|
- </button>
|
|
|
- </view>
|
|
|
+ <!-- 底部按钮 -->
|
|
|
+ <view class="button-group">
|
|
|
+ <button
|
|
|
+ v-if="parindex >= 1 && parindex < topics.ques.length - 1"
|
|
|
+ class="prev-btn"
|
|
|
+ @tap="handlePrevPage(item, parindex)"
|
|
|
+ >
|
|
|
+ 上一题
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ v-if="parindex < topics.ques.length - 1"
|
|
|
+ class="next-btn"
|
|
|
+ @tap="handleNextPage(item, parindex)"
|
|
|
+ >
|
|
|
+ 下一题
|
|
|
+ </button>
|
|
|
</view>
|
|
|
- </template>
|
|
|
- </scroll-view>
|
|
|
- </view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </Container>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref, onMounted } from "vue";
|
|
|
import Questions from "./Questions.vue";
|
|
|
+import Container from "../Container/Container.vue";
|
|
|
|
|
|
const TopicMapList = ["A", "B", "C", "D", "E"];
|
|
|
+const safeArea = ref({});
|
|
|
|
|
|
// Props 定义
|
|
|
const props = defineProps({
|
|
@@ -115,16 +119,17 @@ const props = defineProps({
|
|
|
type: String,
|
|
|
default: "radio",
|
|
|
},
|
|
|
+ mode: {
|
|
|
+ type: String,
|
|
|
+ default: "practice", // practice: 练习模式, exam: 考试模式
|
|
|
+ },
|
|
|
});
|
|
|
|
|
|
// Emits 定义
|
|
|
-const emit = defineEmits(["prevPage", "nextPage"]);
|
|
|
+const emit = defineEmits(["prevPage", "nextPage", "answerChange"]);
|
|
|
|
|
|
// 响应式数据
|
|
|
-const answerList = ref([]);
|
|
|
const nowIndex = ref(0);
|
|
|
-const windowWidth = ref(0);
|
|
|
-const windowHeight = ref(0);
|
|
|
const starIcon = ref("star");
|
|
|
const showAnswer = ref([]);
|
|
|
const selectIndexList = ref([]);
|
|
@@ -132,8 +137,6 @@ const selectIndexList = ref([]);
|
|
|
// 生命周期钩子
|
|
|
onMounted(() => {
|
|
|
const systemInfo = uni.getSystemInfoSync();
|
|
|
- windowWidth.value = systemInfo.windowWidth;
|
|
|
- windowHeight.value = systemInfo.windowHeight;
|
|
|
selectIndexList.value = Array(props.topics.ques.length)
|
|
|
.fill()
|
|
|
.map(() => []);
|
|
@@ -152,17 +155,58 @@ const handleStar = (item) => {
|
|
|
const handleSelect = (value, parindex) => {
|
|
|
const arr = selectIndexList.value[parindex];
|
|
|
const currentTopic = props.topics.ques[parindex];
|
|
|
+ const isSingleChoice =
|
|
|
+ !Array.isArray(currentTopic.anslist) || currentTopic.anslist.length === 1;
|
|
|
+
|
|
|
+ // 如果点击已选中的选项,则取消选择
|
|
|
+ if (arr.includes(value)) {
|
|
|
+ selectIndexList.value[parindex] = arr.filter((item) => item !== value);
|
|
|
+ emit("answerChange", {
|
|
|
+ questionIndex: parindex,
|
|
|
+ answers: selectIndexList.value[parindex],
|
|
|
+ isSingleChoice,
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是考试模式
|
|
|
+ if (props.mode === "exam") {
|
|
|
+ // 如果是单选题,直接替换
|
|
|
+ if (isSingleChoice) {
|
|
|
+ selectIndexList.value[parindex] = [value];
|
|
|
+ } else {
|
|
|
+ // 多选题直接添加
|
|
|
+ arr.push(value);
|
|
|
+ }
|
|
|
+ emit("answerChange", {
|
|
|
+ questionIndex: parindex,
|
|
|
+ answers: selectIndexList.value[parindex],
|
|
|
+ isSingleChoice,
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 练习模式逻辑
|
|
|
const max = Array.isArray(currentTopic.anslist)
|
|
|
? currentTopic.anslist.length
|
|
|
: 1;
|
|
|
|
|
|
- if (arr.includes(value)) return;
|
|
|
- if (arr.length >= max) return;
|
|
|
+ // 如果已达到最大选择数,则不允许继续选择
|
|
|
+ if (arr.length >= max) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (max > 1) {
|
|
|
- arr.push(value);
|
|
|
- } else {
|
|
|
+ // 如果是单选题,直接替换
|
|
|
+ if (max === 1) {
|
|
|
selectIndexList.value[parindex] = [value];
|
|
|
+ } else {
|
|
|
+ // 添加新选择
|
|
|
+ arr.push(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是练习模式,且即将完成所有选择,则显示答案
|
|
|
+ if (props.mode === "practice" && max - 1 === arr.length) {
|
|
|
+ emit("showAnswer", true);
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -202,6 +246,10 @@ const handleNextPage = (item, index) => {
|
|
|
nowIndex.value = index + 1;
|
|
|
emit("nextPage", item, index);
|
|
|
};
|
|
|
+
|
|
|
+const onSafeAreaChange = (s) => {
|
|
|
+ safeArea.value = s;
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
@@ -213,20 +261,10 @@ const handleNextPage = (item, index) => {
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
-.topic-scroll {
|
|
|
- display: flex;
|
|
|
- white-space: nowrap;
|
|
|
- padding: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- -webkit-overflow-scrolling: touch;
|
|
|
-}
|
|
|
-
|
|
|
.topic-item {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 12px;
|
|
|
- padding: 12px;
|
|
|
position: relative;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
@@ -291,10 +329,13 @@ const handleNextPage = (item, index) => {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
align-items: center;
|
|
|
- border-right: 2px solid $uni-primary;
|
|
|
padding-right: 12px;
|
|
|
}
|
|
|
|
|
|
+.border-r-primary {
|
|
|
+ border-right: 2px solid $uni-primary;
|
|
|
+}
|
|
|
+
|
|
|
.answer-text {
|
|
|
color: $uni-primary;
|
|
|
}
|