|
@@ -7,73 +7,60 @@
|
|
|
@onSafeAreaChange="onSafeAreaChange"
|
|
|
:onBack="onBack"
|
|
|
v-bind="$attrs"
|
|
|
- v-if="!open"
|
|
|
+ v-show="!open && !showSheet"
|
|
|
>
|
|
|
- <template v-for="(item, parindex) in data" :key="parindex">
|
|
|
- <view
|
|
|
- :id="`item-${parindex}`"
|
|
|
- v-if="parindex === nowIndex"
|
|
|
- class="topic-item"
|
|
|
- :style="{
|
|
|
- width: `${safeArea.width}px`,
|
|
|
- height: `${safeArea.height}px`,
|
|
|
- flexShrink: 0, // 解决宽度无效问题
|
|
|
- }"
|
|
|
- >
|
|
|
- <!-- 头部 -->
|
|
|
- <view class="topic-header">
|
|
|
- <view class="topic-header-left">
|
|
|
- <view class="topic-type"> {{ styleMap[item.style] }}题 </view>
|
|
|
- <view class="topic-count">
|
|
|
- 第{{ parindex + 1 }}题/共{{ total }}题
|
|
|
- </view>
|
|
|
+ <view
|
|
|
+ :id="`item-${parindex}`"
|
|
|
+ class="topic-item"
|
|
|
+ :style="{
|
|
|
+ width: `${safeArea.width}px`,
|
|
|
+ height: `${safeArea.height}px`,
|
|
|
+ flexShrink: 0, // 解决宽度无效问题
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <!-- 头部 防止定时器不准bug -->
|
|
|
+ <view class="topic-header">
|
|
|
+ <view class="topic-header-left">
|
|
|
+ <view class="topic-type" @click="openSheet">
|
|
|
+ {{ styleMap[data[nowIndex]?.style] }}题
|
|
|
</view>
|
|
|
- <view class="star-icon" @tap="handleStar(item)">
|
|
|
- <uni-icons
|
|
|
- :type="item.star ? 'star-filled' : 'star'"
|
|
|
- size="20"
|
|
|
- color="#fe2624"
|
|
|
- />
|
|
|
- {{ item.star ? "已" : "" }}收藏
|
|
|
+ <view class="topic-count">
|
|
|
+ 第{{ nowIndex + 1 }}题/共{{ total }}题
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <!-- 问题内容 -->
|
|
|
- <view class="topic-content">
|
|
|
- <uvParse
|
|
|
- v-if="item.isImage"
|
|
|
- :content="item.title"
|
|
|
- class="question-text"
|
|
|
- ></uvParse>
|
|
|
- <rich-text
|
|
|
- v-else
|
|
|
- :nodes="item.title"
|
|
|
- class="question-text"
|
|
|
- ></rich-text>
|
|
|
- <uvParse
|
|
|
- :content="item.questions_ex"
|
|
|
- v-if="item.style === 6 && item.isImage"
|
|
|
- class="question-text"
|
|
|
- ></uvParse>
|
|
|
- <rich-text
|
|
|
- v-else
|
|
|
- :nodes="item.questions_ex"
|
|
|
- class="question-text"
|
|
|
- ></rich-text>
|
|
|
- <questions
|
|
|
- v-if="item.style !== 6"
|
|
|
- v-for="(question, index) in item.questions"
|
|
|
- :key="index"
|
|
|
- :answerList="item.ansList"
|
|
|
- :index="index"
|
|
|
- :styleCount="item.style"
|
|
|
- :question="question"
|
|
|
- :showResult="item.showResult"
|
|
|
- :parindex="parindex"
|
|
|
- @select="handleSelect"
|
|
|
- />
|
|
|
- <view class="other" v-else>
|
|
|
+ <uni-countdown
|
|
|
+ :show-day="false"
|
|
|
+ :hour="formatTime(Time.time.startTime, 'hour')"
|
|
|
+ :minute="formatTime(Time.time.startTime, 'minute')"
|
|
|
+ :second="formatTime(Time.time.startTime, 'second')"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ <template v-for="(item, parindex) in data" :key="parindex">
|
|
|
+ <template v-if="parindex === nowIndex">
|
|
|
+ <!-- 问题内容 -->
|
|
|
+ <view class="topic-content">
|
|
|
+ <uvParse
|
|
|
+ v-if="item.isImage"
|
|
|
+ :content="item.title"
|
|
|
+ class="question-text"
|
|
|
+ ></uvParse>
|
|
|
+ <rich-text
|
|
|
+ v-else
|
|
|
+ :nodes="item.title"
|
|
|
+ class="question-text"
|
|
|
+ ></rich-text>
|
|
|
+ <uvParse
|
|
|
+ :content="item.questions_ex"
|
|
|
+ v-if="item.style === 6 && item.isImage"
|
|
|
+ class="question-text"
|
|
|
+ ></uvParse>
|
|
|
+ <rich-text
|
|
|
+ v-else
|
|
|
+ :nodes="item.questions_ex"
|
|
|
+ class="question-text"
|
|
|
+ ></rich-text>
|
|
|
<questions
|
|
|
+ v-if="item.style !== 6"
|
|
|
v-for="(question, index) in item.questions"
|
|
|
:key="index"
|
|
|
:answerList="item.ansList"
|
|
@@ -84,78 +71,123 @@
|
|
|
:parindex="parindex"
|
|
|
@select="handleSelect"
|
|
|
/>
|
|
|
+ <view class="other" v-else>
|
|
|
+ <questions
|
|
|
+ v-for="(question, index) in item.questions"
|
|
|
+ :key="index"
|
|
|
+ :answerList="item.ansList"
|
|
|
+ :index="index"
|
|
|
+ :styleCount="item.style"
|
|
|
+ :question="question"
|
|
|
+ :showResult="item.showResult"
|
|
|
+ :parindex="parindex"
|
|
|
+ @select="handleSelect"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
|
- <!-- 答案展示 -->
|
|
|
- <view v-if="item.showResult" class="answer-section">
|
|
|
- <view class="answer-content">
|
|
|
- <view class="answer-row">
|
|
|
- <view class="answer-item border-r-primary">
|
|
|
- 正确答案:
|
|
|
- <text class="answer-text">{{
|
|
|
- item.ansList.map((i) => i.label).join("")
|
|
|
- }}</text>
|
|
|
+ <!-- 答案展示 -->
|
|
|
+ <view v-if="item.showResult" class="answer-section">
|
|
|
+ <view class="answer-content">
|
|
|
+ <view class="answer-row">
|
|
|
+ <view class="answer-item border-r-primary">
|
|
|
+ 正确答案:
|
|
|
+ <text class="answer-text">{{
|
|
|
+ item.ansList.map((i) => i.label).join("")
|
|
|
+ }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="answer-item">
|
|
|
+ 我的答案:
|
|
|
+ <text class="answer-text">{{ item.selectAns.join("") }}</text>
|
|
|
+ </view>
|
|
|
+ <view
|
|
|
+ class="tip"
|
|
|
+ :style="{
|
|
|
+ color: item.isRight ? '#00be00' : '#f00',
|
|
|
+ }"
|
|
|
+ >{{ item.isRight ? "太棒了~" : "再接再励!" }}</view
|
|
|
+ >
|
|
|
</view>
|
|
|
- <view class="answer-item">
|
|
|
- 我的答案:
|
|
|
- <text class="answer-text">{{
|
|
|
- item.selectAns.map((i) => i.value).join("")
|
|
|
- }}</text>
|
|
|
+ <view class="parsing">
|
|
|
+ <p>解析:</p>
|
|
|
+ <scroll-view
|
|
|
+ :show-scrollbar="false"
|
|
|
+ scroll-y
|
|
|
+ class="parsing-text"
|
|
|
+ >
|
|
|
+ <rich-text :nodes="item.explain"></rich-text>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 底部按钮 -->
|
|
|
+ <view class="button-group">
|
|
|
+ <button
|
|
|
+ v-if="parindex >= 1"
|
|
|
+ class="prev-btn"
|
|
|
+ @tap="handlePage(item, parindex, 'prevPage')"
|
|
|
+ >
|
|
|
+ 上一题
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ v-if="parindex + 1 < total"
|
|
|
+ class="next-btn"
|
|
|
+ @tap="handlePage(item, parindex, 'nextPage')"
|
|
|
+ >
|
|
|
+ 下一题
|
|
|
+ </button>
|
|
|
+ <button v-if="parindex + 1 === total" @click="examEnd">交卷</button>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </view>
|
|
|
+ </Container>
|
|
|
+ <uni-popup
|
|
|
+ ref="bottomModal"
|
|
|
+ @change="(e) => (showSheet = e.show)"
|
|
|
+ type="bottom"
|
|
|
+ backgroundColor="#fff"
|
|
|
+ borderRadius="10px 10px 0 0"
|
|
|
+ class="bottom-modal"
|
|
|
+ >
|
|
|
+ <view class="bottom-modal" v-if="showSheet">
|
|
|
+ <view class="title">
|
|
|
+ <view>答题卡</view>
|
|
|
+ <uni-icons
|
|
|
+ class="closeempty"
|
|
|
+ type="closeempty"
|
|
|
+ @click="bottomModal?.close"
|
|
|
+ ></uni-icons>
|
|
|
+ </view>
|
|
|
+ <view class="content">
|
|
|
+ <scroll-view class="scroll-view" scroll-y>
|
|
|
+ <view v-for="(key, index) in styleMap" class="ques-type" :key="key">
|
|
|
+ <view>{{ key }}题</view>
|
|
|
+ <view class="grid">
|
|
|
<view
|
|
|
- class="tip"
|
|
|
- :style="{
|
|
|
- color: item.isRight ? '#00be00' : '#f00',
|
|
|
+ v-for="item in data.filter((i) => i.style === +index)"
|
|
|
+ :key="item.id"
|
|
|
+ class="item"
|
|
|
+ :class="{
|
|
|
+ success: !!item.selectAns.length,
|
|
|
}"
|
|
|
- >{{ item.isRight ? "太棒了~" : "再接再励!" }}</view
|
|
|
+ @click="
|
|
|
+ () => {
|
|
|
+ nowIndex = item.ind;
|
|
|
+ bottomModal?.close();
|
|
|
+ }
|
|
|
+ "
|
|
|
>
|
|
|
- </view>
|
|
|
- <view class="parsing">
|
|
|
- <p>解析:</p>
|
|
|
- <scroll-view
|
|
|
- :show-scrollbar="false"
|
|
|
- scroll-y
|
|
|
- class="parsing-text"
|
|
|
- >
|
|
|
- <rich-text :nodes="item.explain"></rich-text>
|
|
|
- </scroll-view>
|
|
|
+ {{ item.ind + 1 }}
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 底部按钮 -->
|
|
|
- <view class="button-group">
|
|
|
- <button
|
|
|
- v-if="parindex >= 1 && item.showResult"
|
|
|
- class="prev-btn"
|
|
|
- @tap="handlePage(item, parindex, 'prevPage')"
|
|
|
- >
|
|
|
- 上一题
|
|
|
- </button>
|
|
|
- <button
|
|
|
- v-if="parindex + 1 < total && item.showResult"
|
|
|
- class="next-btn"
|
|
|
- @tap="handlePage(item, parindex, 'nextPage')"
|
|
|
- >
|
|
|
- 下一题
|
|
|
- </button>
|
|
|
- <button
|
|
|
- v-if="parindex + 1 === total && item.showResult"
|
|
|
- @click="emit('lookReport', data, submitter)"
|
|
|
- >
|
|
|
- 查看报告
|
|
|
- </button>
|
|
|
- <button
|
|
|
- v-if="!item.showResult"
|
|
|
- @click="questionSubmit(item, parindex)"
|
|
|
- >
|
|
|
- 提交
|
|
|
- </button>
|
|
|
- </view>
|
|
|
+ </scroll-view>
|
|
|
+ <button @click="submit">交卷</button>
|
|
|
</view>
|
|
|
- </template>
|
|
|
- </Container>
|
|
|
+ </view>
|
|
|
+ </uni-popup>
|
|
|
<Modal
|
|
|
v-model:open="open"
|
|
|
title="温馨提示"
|
|
@@ -174,23 +206,54 @@ import Questions from "./Questions.vue";
|
|
|
import Container from "../Container/Container.vue";
|
|
|
import Modal from "../Modal/Modal.vue";
|
|
|
import uvParse from "@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue";
|
|
|
+import { useTimeStore } from "@/store/time";
|
|
|
const styleMap = {
|
|
|
2: "单选",
|
|
|
3: "多选",
|
|
|
6: "配伍",
|
|
|
};
|
|
|
-
|
|
|
-const submitter = ref({
|
|
|
- text: "提交查看",
|
|
|
- closeText: "直接退出",
|
|
|
- context: "您本次答题还未提交, 确定要退出答题吗?",
|
|
|
- isEndQuestion: false,
|
|
|
-});
|
|
|
-
|
|
|
const safeArea = ref({}); // 安全区域
|
|
|
const data = ref([]); // 题目数据
|
|
|
const open = ref(false); // 是否显示弹窗
|
|
|
+const showSheet = ref(false); // 是否显示底部弹窗
|
|
|
let r = null; // 异步方法
|
|
|
+const Time = useTimeStore();
|
|
|
+const bottomModal = ref(null); // 底部弹窗
|
|
|
+
|
|
|
+const openSheet = () => {
|
|
|
+ bottomModal.value.open();
|
|
|
+};
|
|
|
+const submit = () => {
|
|
|
+ const len = data.value.filter((item) => !item.selectAns.length).length;
|
|
|
+ if (len) {
|
|
|
+ bottomModal.value.close();
|
|
|
+ submitter.value = {
|
|
|
+ ...submitter.value,
|
|
|
+ closeText: "提交试卷",
|
|
|
+ context: `您还有${len}道题没做,确认交卷吗?`,
|
|
|
+ };
|
|
|
+ open.value = true;
|
|
|
+ }
|
|
|
+};
|
|
|
+const formatTime = (time, type) => {
|
|
|
+ // 获取time一个半小时后的时间戳
|
|
|
+ const timeStamp = new Date(time + 1.5 * 60 * 60 * 1000);
|
|
|
+ const date = new Date();
|
|
|
+ switch (type) {
|
|
|
+ case "hour":
|
|
|
+ return timeStamp.getHours() - date.getHours();
|
|
|
+ case "minute":
|
|
|
+ return timeStamp.getMinutes() - date.getMinutes();
|
|
|
+ case "second":
|
|
|
+ return timeStamp.getSeconds() - date.getSeconds();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const submitter = ref({
|
|
|
+ text: "继续考试",
|
|
|
+ closeText: "直接退出",
|
|
|
+ context: "确定退出考试吗?退出后将不保留本次考试记录,确定要放弃吗?",
|
|
|
+});
|
|
|
|
|
|
// Props 定义
|
|
|
const props = defineProps({
|
|
@@ -223,27 +286,33 @@ const emit = defineEmits([
|
|
|
|
|
|
const onBack = () =>
|
|
|
new Promise((resolve) => {
|
|
|
- const isEndQuestion =
|
|
|
- nowIndex.value === props.total - 1 &&
|
|
|
- data.value[nowIndex.value].showResult;
|
|
|
-
|
|
|
- if (isEndQuestion) {
|
|
|
- submitter.value = {
|
|
|
- ...submitter.value,
|
|
|
- text: "查看报告",
|
|
|
- context:
|
|
|
- "您的答题报告已准备就绪!退出后将无法查看本次分析结果,且不保留本次答题记录,确定要放弃吗?",
|
|
|
- isEndQuestion,
|
|
|
- };
|
|
|
+ if (!data.value.length) {
|
|
|
+ resolve(true);
|
|
|
+ return;
|
|
|
}
|
|
|
open.value = true;
|
|
|
r = resolve;
|
|
|
});
|
|
|
|
|
|
-const onClose = () => r(true);
|
|
|
+const examEnd = () => {
|
|
|
+ data.value = data.value.map((item) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ showResult: true,
|
|
|
+ isRight: isRight(
|
|
|
+ item.selectAns,
|
|
|
+ item.ansList.map((q) => q.label)
|
|
|
+ ),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ emit("lookReport", data.value, submitter.value);
|
|
|
+};
|
|
|
+const onClose = () => {
|
|
|
+ if (submitter.value.closeText !== "提交试卷") return r(true);
|
|
|
+ examEnd();
|
|
|
+};
|
|
|
const onSubmit = () =>
|
|
|
new Promise((res) => {
|
|
|
- emit("lookReport", data.value, submitter.value);
|
|
|
res(true);
|
|
|
});
|
|
|
// 响应式数据
|
|
@@ -251,20 +320,10 @@ const nowIndex = ref(0);
|
|
|
|
|
|
// 判断答案是否正确
|
|
|
const isRight = (selectAns, rightAns) => {
|
|
|
+ if (!selectAns.length) return;
|
|
|
return selectAns.every((item) => rightAns.includes(item));
|
|
|
};
|
|
|
|
|
|
-// 收藏方法
|
|
|
-const handleStar = (item) => {
|
|
|
- if (!props.onStar) return;
|
|
|
- props.onStar(item).then((res) => {
|
|
|
- uni.showToast({
|
|
|
- title: res ? "已加入收藏" : "已移除收藏",
|
|
|
- icon: "none",
|
|
|
- });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
const handleSelect = ({ pid, checked, index, style }) => {
|
|
|
// 如果不是多选,就取消其他的选项
|
|
|
if (style !== 3) {
|
|
@@ -281,7 +340,6 @@ const handleSelect = ({ pid, checked, index, style }) => {
|
|
|
[item.value],
|
|
|
data.value[pid].ansList.map((q) => q.label)
|
|
|
);
|
|
|
-
|
|
|
|
|
|
// 更新答案
|
|
|
data.value[pid].selectAns = data.value[pid].questions
|
|
@@ -289,15 +347,6 @@ const handleSelect = ({ pid, checked, index, style }) => {
|
|
|
.map((q) => q.value);
|
|
|
};
|
|
|
|
|
|
-const questionSubmit = (item) => {
|
|
|
- item.showResult = true;
|
|
|
- item.selectAns = item.questions.filter((q) => q.checked);
|
|
|
- item.isRight = isRight(
|
|
|
- item.selectAns.map((q) => q.value),
|
|
|
- item.ansList.map((q) => q.label)
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
const handlePage = (item, index, type) => {
|
|
|
nowIndex.value = index + (type === "prevPage" ? -1 : 1);
|
|
|
emit(type, { item, index });
|
|
@@ -314,6 +363,46 @@ const onSafeAreaChange = (s) => {
|
|
|
display: flex;
|
|
|
gap: 12px;
|
|
|
}
|
|
|
+.scroll-view {
|
|
|
+ height: 50vh;
|
|
|
+}
|
|
|
+.content {
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333333;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 24rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
+
|
|
|
+ .ques-type {
|
|
|
+ display: flex;
|
|
|
+ gap: 16rpx;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+}
|
|
|
+.grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(5, 1fr);
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+.bottom-modal {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+}
|
|
|
+.title {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+ .closeempty {
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
.parsing-text {
|
|
|
height: 381rpx;
|
|
|
white-space: normal;
|
|
@@ -353,6 +442,20 @@ const onSafeAreaChange = (s) => {
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
+.item {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border: 1rpx solid #dddddd;
|
|
|
+ &.success {
|
|
|
+ border-color: $success;
|
|
|
+ color: $success;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.topic-type {
|
|
|
border: 1px solid $uni-primary;
|
|
|
padding: 0 8px;
|