Browse Source

Merge branch 'feature-home-0.0.2'

huangziyang 1 week ago
parent
commit
1944b58b95

+ 270 - 167
components/Topic/TopicExam.vue

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

+ 4 - 1
components/Topic/TopicPractice.vue

@@ -223,6 +223,10 @@ const emit = defineEmits([
 
 const onBack = () =>
   new Promise((resolve) => {
+    if (!data.value.length) {
+      resolve(true);
+      return;
+    }
     const isEndQuestion =
       nowIndex.value === props.total - 1 &&
       data.value[nowIndex.value].showResult;
@@ -281,7 +285,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

+ 2 - 2
pages.json

@@ -1,7 +1,7 @@
 {
   "pages": [
     {
-      "path": "pages/home/index",
+      "path": "pages/index/index",
       "style": {
         "navigationStyle": "custom",
         "navigationBarTitleText": "首页"
@@ -113,7 +113,7 @@
     "backgroundColor": "#ffffff",
     "list": [
       {
-        "pagePath": "pages/home/index",
+        "pagePath": "pages/index/index",
         "iconPath": "static/icons/home.png",
         "selectedIconPath": "static/icons/home_select.png",
         "text": "首页"

+ 0 - 0
pages/home/index.vue → pages/index/index.vue


+ 8 - 5
pages/real/exam.vue

@@ -43,12 +43,13 @@ const getList = async (params) => {
     "api/question_bank/question_reception/topic/get_chapter_topic",
     {
       ...params,
-      id: getRoute().params.id,
+      // id: getRoute().params.id,
+      id: 66,
     }
   );
   total.value = res.data.total;
   data.value.push(
-    ...res.data.data.map((item) => {
+    ...res.data.data.map((item, ind) => {
       let questions = [];
       const ans = item.correct_answer.split(",");
       const ansList = [];
@@ -79,6 +80,7 @@ const getList = async (params) => {
         showResult: false, // 是否展示答案
         isRight: false, // 是否正确
         isImage: item.title.includes("<img"),
+        ind,
       };
     })
   );
@@ -100,7 +102,7 @@ const lookReport = (d, s) => {
     totalTime,
   };
   const r = data.value.filter((item) => item.isRight).length;
-  const n = data.value.filter((item) => !item.showResult).length;
+  const n = data.value.filter((item) => !item.selectAns.length).length;
   correct.value = {
     rate: (r / total.value) * 100,
     right: r,
@@ -125,6 +127,7 @@ onMounted(() => {
     :topics="data"
     @nextPage="nextPage"
     @lookReport="lookReport"
+    :empty="!data.length"
     border
     v-if="!showReport"
   />
@@ -155,7 +158,7 @@ onMounted(() => {
           class="item"
           :class="{
             right: it.isRight && it.showResult,
-            error: !it.isRight && it.showResult,
+            error: !it.isRight && it.showResult && it.selectAns.length,
           }"
           v-for="(it, index) in data"
           :key="it.id"
@@ -165,7 +168,7 @@ onMounted(() => {
     </view>
     <template #footer>
       <view class="footer">
-        <view class="button plain">答题解析</view>
+        <view class="button plain" @click="showReport = false">答题解析</view>
         <view class="button">炫耀一下</view>
       </view>
     </template>

+ 2 - 2
pages/regulations/index.vue

@@ -150,10 +150,10 @@ const onSafeAreaChange = (s) => {
 
 const onChnage = () => {
   resolveHeight();
-};
+}
 
 const onClickMask = () => {
-  showContainer.value = false;
+  showContainer.value = true;
 };
 
 onMounted(async () => {