|
@@ -1,59 +1,75 @@
|
|
|
+view
|
|
|
<template>
|
|
|
- <view class="topic-container">
|
|
|
- <scroll-view
|
|
|
- scroll-x
|
|
|
- :scroll-into-view="`item-${nowIndex}`"
|
|
|
- class="topic-scroll"
|
|
|
- >
|
|
|
+ <Container
|
|
|
+ :scrollX="true"
|
|
|
+ :scrollY="false"
|
|
|
+ :scroll-into-view="`item-${nowIndex}`"
|
|
|
+ @onSafeAreaChange="onSafeAreaChange"
|
|
|
+ >
|
|
|
+ <template v-for="(item, parindex) in topics.ques" :key="parindex">
|
|
|
<view
|
|
|
- v-for="(item, parindex) in topics"
|
|
|
- :key="parindex"
|
|
|
:id="`item-${parindex}`"
|
|
|
class="topic-item"
|
|
|
:style="{
|
|
|
- flexShrink: 0
|
|
|
+ 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">
|
|
|
- {{ answerList[parindex]?.length > 1 ? '多选题' : '单选题' }}
|
|
|
+ {{
|
|
|
+ item.mode || (item.anslist?.length > 1 ? "多选题" : "单选题")
|
|
|
+ }}
|
|
|
</view>
|
|
|
<view class="topic-count">
|
|
|
- 第{{ parindex + 1 }}题/共{{ topics.length }}题
|
|
|
+ 第{{ parindex + 1 }}题/共{{ topics.ques.length }}题
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="star-icon" @tap="handleStar(item)">
|
|
|
- <text class="iconfont" :class="starIcon === 'star' ? 'icon-star' : 'icon-star-filled'"></text>
|
|
|
+ <text
|
|
|
+ class="iconfont"
|
|
|
+ :class="starIcon === 'star' ? 'icon-star' : 'icon-star-filled'"
|
|
|
+ ></text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 问题内容 -->
|
|
|
<view class="topic-content">
|
|
|
- <view class="question-text">请问图片里那个才是党参?</view>
|
|
|
+ <view class="question-text">{{ item.title }}</view>
|
|
|
<questions
|
|
|
- v-for="(_, index) in 5"
|
|
|
+ v-for="(question, index) in item.questions"
|
|
|
:key="index"
|
|
|
- :answer-list="answerList[parindex] || []"
|
|
|
+ :answer-list="
|
|
|
+ Array.isArray(item.anslist) ? item.anslist : [item.anslist]
|
|
|
+ "
|
|
|
:index="index"
|
|
|
- :select-count="selectIndexList"
|
|
|
+ :select-count="selectIndexList[parindex] || []"
|
|
|
+ :question="question"
|
|
|
+ :parindex="parindex"
|
|
|
+ :mode="mode"
|
|
|
@select="handleSelect"
|
|
|
- @show-answer="setShowAnswer"
|
|
|
+ @show-answer="(index) => setShowAnswer(index, parindex)"
|
|
|
/>
|
|
|
</view>
|
|
|
|
|
|
<!-- 答案展示 -->
|
|
|
- <view v-if="showAnswer" class="answer-section">
|
|
|
+ <view
|
|
|
+ v-if="showAnswer[parindex] && mode === 'practice'"
|
|
|
+ class="answer-section"
|
|
|
+ >
|
|
|
<view class="answer-content">
|
|
|
<view class="answer-row">
|
|
|
- <view class="answer-item">
|
|
|
+ <view class="answer-item border-r-primary">
|
|
|
正确答案:
|
|
|
<text class="answer-text">{{ getRightAnswer(parindex) }}</text>
|
|
|
</view>
|
|
|
<view class="answer-item">
|
|
|
我的答案:
|
|
|
- <text class="answer-text">{{ getMyAnswer() }}</text>
|
|
|
+ <text class="answer-text">{{ getMyAnswer(parindex) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
@@ -62,14 +78,14 @@
|
|
|
<!-- 底部按钮 -->
|
|
|
<view class="button-group">
|
|
|
<button
|
|
|
- v-if="parindex >= 1 && parindex < topics.length - 1"
|
|
|
+ v-if="parindex >= 1 && parindex < topics.ques.length - 1"
|
|
|
class="prev-btn"
|
|
|
@tap="handlePrevPage(item, parindex)"
|
|
|
>
|
|
|
上一题
|
|
|
</button>
|
|
|
<button
|
|
|
- v-if="parindex < topics.length - 1"
|
|
|
+ v-if="parindex < topics.ques.length - 1"
|
|
|
class="next-btn"
|
|
|
@tap="handleNextPage(item, parindex)"
|
|
|
>
|
|
@@ -77,112 +93,180 @@
|
|
|
</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
- </scroll-view>
|
|
|
- </view>
|
|
|
+ </template>
|
|
|
+ </Container>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted } from 'vue'
|
|
|
-import Questions from './Questions.vue'
|
|
|
+import { ref, onMounted } from "vue";
|
|
|
+import Questions from "./Questions.vue";
|
|
|
+import Container from "../Container/Container.vue";
|
|
|
|
|
|
-const TopicMapList = ['A', 'B', 'C', 'D', 'E']
|
|
|
+const TopicMapList = ["A", "B", "C", "D", "E"];
|
|
|
+const safeArea = ref({});
|
|
|
|
|
|
// Props 定义
|
|
|
const props = defineProps({
|
|
|
topics: {
|
|
|
- type: Array,
|
|
|
- default: () => Array(3).fill(0)
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
},
|
|
|
onStar: {
|
|
|
type: Function,
|
|
|
- default: null
|
|
|
+ default: null,
|
|
|
},
|
|
|
type: {
|
|
|
type: String,
|
|
|
- default: 'radio'
|
|
|
- }
|
|
|
-})
|
|
|
+ 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(false)
|
|
|
-const selectIndexList = ref([])
|
|
|
+const nowIndex = ref(0);
|
|
|
+const starIcon = ref("star");
|
|
|
+const showAnswer = ref([]);
|
|
|
+const selectIndexList = ref([]);
|
|
|
|
|
|
// 生命周期钩子
|
|
|
onMounted(() => {
|
|
|
- const systemInfo = uni.getSystemInfoSync()
|
|
|
- windowWidth.value = systemInfo.windowWidth
|
|
|
- windowHeight.value = systemInfo.windowHeight
|
|
|
- answerList.value = [[1, 3], [2], [3]]
|
|
|
-})
|
|
|
+ const systemInfo = uni.getSystemInfoSync();
|
|
|
+ selectIndexList.value = Array(props.topics.ques.length)
|
|
|
+ .fill()
|
|
|
+ .map(() => []);
|
|
|
+ showAnswer.value = Array(props.topics.ques.length).fill(false);
|
|
|
+});
|
|
|
|
|
|
// 方法
|
|
|
const handleStar = (item) => {
|
|
|
- if (!props.onStar) return
|
|
|
- props.onStar(item).then(res => {
|
|
|
- if (!res) return
|
|
|
- starIcon.value = starIcon.value === 'star' ? 'star-2' : 'star'
|
|
|
- })
|
|
|
-}
|
|
|
+ if (!props.onStar) return;
|
|
|
+ props.onStar(item).then((res) => {
|
|
|
+ if (!res) return;
|
|
|
+ starIcon.value = starIcon.value === "star" ? "star-2" : "star";
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+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;
|
|
|
+ }
|
|
|
|
|
|
-const handleSelect = (index) => {
|
|
|
- selectIndexList.value.push(index)
|
|
|
-}
|
|
|
+ // 如果是考试模式
|
|
|
+ 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 setShowAnswer = (value) => {
|
|
|
- showAnswer.value = value
|
|
|
-}
|
|
|
+ // 练习模式逻辑
|
|
|
+ const max = Array.isArray(currentTopic.anslist)
|
|
|
+ ? currentTopic.anslist.length
|
|
|
+ : 1;
|
|
|
|
|
|
-const getRightAnswer = (index) => {
|
|
|
- return (answerList.value[index] || [])
|
|
|
- .map(item => TopicMapList[item])
|
|
|
- .join('')
|
|
|
-}
|
|
|
+ // 如果已达到最大选择数,则不允许继续选择
|
|
|
+ if (arr.length >= max) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
-const getMyAnswer = () => {
|
|
|
- return selectIndexList.value
|
|
|
- .map(item => TopicMapList[item])
|
|
|
- .join('')
|
|
|
-}
|
|
|
+ // 如果是单选题,直接替换
|
|
|
+ if (max === 1) {
|
|
|
+ selectIndexList.value[parindex] = [value];
|
|
|
+ } else {
|
|
|
+ // 添加新选择
|
|
|
+ arr.push(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是练习模式,且即将完成所有选择,则显示答案
|
|
|
+ if (props.mode === "practice" && max - 1 === arr.length) {
|
|
|
+ emit("showAnswer", true);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const setShowAnswer = (value, parindex) => {
|
|
|
+ showAnswer.value[parindex] = value;
|
|
|
+};
|
|
|
+
|
|
|
+const getRightAnswer = (index) => {
|
|
|
+ const topic = props.topics.ques[index];
|
|
|
+ const answers = Array.isArray(topic.anslist)
|
|
|
+ ? topic.anslist
|
|
|
+ : [topic.anslist];
|
|
|
+ return answers
|
|
|
+ .map((value) => {
|
|
|
+ const question = topic.questions.find((q) => q.value === value);
|
|
|
+ return question ? TopicMapList[topic.questions.indexOf(question)] : "";
|
|
|
+ })
|
|
|
+ .join("");
|
|
|
+};
|
|
|
+
|
|
|
+const getMyAnswer = (parindex) => {
|
|
|
+ const topic = props.topics.ques[parindex];
|
|
|
+ return (selectIndexList.value[parindex] || [])
|
|
|
+ .map((value) => {
|
|
|
+ const question = topic.questions.find((q) => q.value === value);
|
|
|
+ return question ? TopicMapList[topic.questions.indexOf(question)] : "";
|
|
|
+ })
|
|
|
+ .join("");
|
|
|
+};
|
|
|
|
|
|
const handlePrevPage = (item, index) => {
|
|
|
- nowIndex.value = index - 1
|
|
|
- emit('prevPage', item, index)
|
|
|
-}
|
|
|
+ nowIndex.value = index - 1;
|
|
|
+ emit("prevPage", item, index);
|
|
|
+};
|
|
|
|
|
|
const handleNextPage = (item, index) => {
|
|
|
- nowIndex.value = index + 1
|
|
|
- emit('nextPage', item, index)
|
|
|
-}
|
|
|
+ nowIndex.value = index + 1;
|
|
|
+ emit("nextPage", item, index);
|
|
|
+};
|
|
|
+
|
|
|
+const onSafeAreaChange = (s) => {
|
|
|
+ safeArea.value = s;
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-@import '@/uni.scss';
|
|
|
+@import "@/uni.scss";
|
|
|
|
|
|
.topic-container {
|
|
|
width: 100vw;
|
|
|
overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.topic-scroll {
|
|
|
- display: flex;
|
|
|
- white-space: nowrap;
|
|
|
- padding: 0;
|
|
|
+ position: relative;
|
|
|
}
|
|
|
|
|
|
.topic-item {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 12px;
|
|
|
- padding: 12px;
|
|
|
position: relative;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
.topic-header {
|
|
@@ -226,7 +310,7 @@ const handleNextPage = (item, index) => {
|
|
|
.answer-section {
|
|
|
flex: 1;
|
|
|
border-radius: 16rpx;
|
|
|
- border: 1px solid $uni-primary;
|
|
|
+ border: 1px solid #ddd;
|
|
|
padding: 24rpx;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
@@ -245,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;
|
|
|
}
|
|
@@ -272,4 +359,4 @@ const handleNextPage = (item, index) => {
|
|
|
background-color: $uni-primary;
|
|
|
color: #fff;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|