2 Commits 9d03ff495c ... e19f83b650

Auteur SHA1 Message Date
  huangziyang e19f83b650 Merge branch 'feature-home-0.0.3' il y a 1 semaine
  huangziyang 1b99a80a42 feat: 考试流程对接 il y a 1 semaine

+ 0 - 4
App.vue

@@ -17,10 +17,6 @@ export default {
 @import "@/uni.scss";
 /*每个页面公共css */
 @import "@/uni_modules/uni-scss/index.scss";
-@font-face {
-  font-family: PingFang SC;
-  src: url('./static/PingFangBold.ttf');
-}
 button {
   background-color: $primary;
   font-weight: 500;

+ 4 - 4
components/Container/Container.vue

@@ -72,7 +72,7 @@ const props = defineProps({
   bottomBorder: {
     type: Boolean,
     default: false,
-  }
+  },
 });
 
 const emit = defineEmits(["onSafeAreaChange", "onScroll"]);
@@ -158,9 +158,9 @@ defineExpose({
 
 <template>
   <view class="container" :style="{ background: bgColor }">
-    <div class="bg">
+    <view class="bg" v-if="$slots.bg">
       <slot name="bg"></slot>
-    </div>
+    </view>
     <view
       class="title"
       :style="{
@@ -218,7 +218,7 @@ defineExpose({
       :style="{
         width: `${safeArea.width}px`,
         background: bottomBgColor,
-        borderTop: bottomBorder? '1px solid #eee' : 'none',
+        borderTop: bottomBorder ? '1px solid #eee' : 'none',
       }"
     >
       <slot name="footer" :footer-height="footerHeight" />

+ 149 - 12
components/ShareTemplate/ShareTemplate.vue

@@ -1,19 +1,156 @@
-<script setup>
-import { ref, onMounted } from "vue";
+<script setup name="shareTemplate">
+import lPainter from "@/uni_modules/lime-painter/components/l-painter/l-painter.vue";
+import lPainterView from "@/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue";
+import lPainterImage from "@/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue";
+import lPainterText from "@/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue";
+import { ref } from "vue";
+
+const painter = ref(null);
 const prosp = defineProps({
+  onClose: {
+    type: Function,
+    default: () => {},
+  },
+  bg: {
+    type: String,
+    default: "",
+  },
+  correct: {
+    type: Object,
+    default: () => ({}),
+  },
+  onSuccess: {
+    type: Function,
+    default: () => {},
+  }
 });
 
-const poster = ref({});
-
-onMounted(() => {
-  poster.value = {
-    css: {
-      width: 750,
+const onSaveImage = () => {
+  painter.value?.canvasToTempFilePathSync({
+    fileType: "jpg",
+    pathType: "url",
+    quality: 1,
+    success: (res) => {
+      // 非H5 保存到相册
+      uni.saveImageToPhotosAlbum({
+        filePath: res.tempFilePath,
+        success: function () {
+          uni.showToast({
+            title: "保存成功",
+            icon: "success",
+          });
+          prosp.onClose?.();
+        },
+      });
     },
-    views: {},
-  };
-});
+  });
+};
 </script>
 <template>
-  <l-painter :board="poster" />
+  <view class="share-template">
+    <view class="share-img">
+      <l-painter ref="painter">
+        <l-painter-view
+          css="width: 620rpx; height: 889rpx; position: relative;"
+        >
+          <l-painter-image
+            :src="bg"
+            css="width: 620rpx; height: 889rpx; position: absolute;top: 0;z-index: 1;"
+          />
+          <l-painter-text
+            :text="correct.right + '题'"
+            css="position: absolute;top: 350rpx;left: 280rpx;color: #3e8647; z-index: 2;"
+          />
+          <l-painter-text
+            :text="correct.total + '题'"
+            css="position: absolute;top: 265rpx;color: #e7a552;left: 255rpx; z-index: 2;"
+          />
+          <l-painter-text
+            :text="correct.error + '题'"
+            css="position: absolute;top: 440rpx;left: 290rpx;color: red; z-index: 2;"
+          />
+          <l-painter-text
+            :text="correct.rate.toFixed(1) + '%'"
+            css="position: absolute;top: 550rpx;left: 100rpx;color: #e7a552;font-size: 30rpx; z-index: 2;"
+          />
+          <l-painter-view css="background: red; width: 146rpx; height: 146rpx;position: absolute;bottom: 18rpx;left: 18rpx;">
+
+          </l-painter-view>
+        </l-painter-view>
+      </l-painter>
+    </view>
+    <view class="footer">
+      <div class="title">
+        <view>分享更多好友</view>
+        <uni-icons type="closeempty" class="closeempty" @click="onClose" />
+      </div>
+      <view class="items">
+        <view class="save-img" @click="onSaveImage">
+          <image
+            src="https://openwork-oss.oss-cn-shenzhen.aliyuncs.com/uploads/question/2025/05/zGBXaE27pgRjEA6NoMLC75CSdMs3BKjh1vnpGKAc.png"
+            mode="scaleToFill"
+            class="save"
+          />
+          <text>保存图片</text>
+        </view>
+      </view>
+    </view>
+  </view>
 </template>
+
+<style scoped lang="scss">
+.share-template {
+  height: 100vh;
+  width: 100%;
+  background: #999999;
+  position: relative;
+  .share-img {
+    position: absolute;
+    top: 368rpx;
+    left: 65rpx;
+  }
+
+  .footer {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: calc(68rpx + 80rpx + 140rpx);
+    background-color: #fff;
+    border-radius: 40rpx 40rpx 0rpx 0rpx;
+
+    .title {
+      display: flex;
+      height: 80rpx;
+      align-items: center;
+      justify-content: center;
+      position: relative;
+
+      .closeempty {
+        position: absolute;
+        right: 32rpx;
+      }
+    }
+
+    .items {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      height: 140rpx;
+      .save-img {
+        display: flex;
+        align-items: center;
+        gap: 12rpx;
+        font-weight: 400;
+        font-size: 28rpx;
+        color: #000000;
+
+        .save {
+          width: 60rpx;
+          height: 60rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 6 - 3
components/Topic/TopicExam.vue

@@ -10,7 +10,7 @@
     v-show="!open && !showSheet"
   >
     <view
-      :id="`item-${parindex}`"
+      :id="`item-${nowIndex}`"
       class="topic-item"
       :style="{
         width: `${safeArea.width}px`,
@@ -293,13 +293,16 @@ const onBack = () =>
     open.value = true;
     r = resolve;
   });
-
+const ansIsRight = (selectAns, rightAns) => {
+  if (selectAns.length !== rightAns.length) return false;
+  return isRight(selectAns, rightAns);
+};
 const examEnd = () => {
   data.value = data.value.map((item) => {
     return {
       ...item,
       showResult: true,
-      isRight: isRight(
+      isRight: ansIsRight(
         item.selectAns,
         item.ansList.map((q) => q.label)
       ),

+ 132 - 125
components/Topic/TopicPractice.vue

@@ -9,71 +9,60 @@
     v-bind="$attrs"
     v-if="!open"
   >
-    <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-${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[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="star-icon" @tap="handleStar(data[nowIndex])">
+          <uni-icons
+            :type="data[nowIndex]?.is_favorite ? 'star-filled' : 'star'"
+            size="20"
+            color="#fe2624"
           />
-          <view class="other" v-else>
+          {{ data[nowIndex]?.is_favorite ? "已" : "" }}收藏
+        </view>
+      </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,77 +73,90 @@
               :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.map((i) => i.value).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
-                class="tip"
-                :style="{
-                  color: item.isRight ? '#00be00' : '#f00',
-                }"
-                >{{ item.isRight ? "太棒了~" : "再接再励!" }}</view
-              >
-            </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>
             </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>
-      </view>
-    </template>
+          <!-- 底部按钮 -->
+          <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>
+        </template>
+      </template>
+    </view>
   </Container>
   <Modal
     v-model:open="open"
@@ -292,10 +294,15 @@ const handleSelect = ({ pid, checked, index, style }) => {
     .map((q) => q.value);
 };
 
+const ansIsRight = (selectAns, rightAns) => {
+  if (selectAns.length !== rightAns.length) return false;
+  return isRight(selectAns, rightAns);
+};
+
 const questionSubmit = (item) => {
   item.showResult = true;
   item.selectAns = item.questions.filter((q) => q.checked);
-  item.isRight = isRight(
+  item.isRight = ansIsRight(
     item.selectAns.map((q) => q.value),
     item.ansList.map((q) => q.label)
   );

+ 6 - 6
components/Tree/Tree.vue

@@ -11,7 +11,7 @@ const props = defineProps({
       value: "id",
       label: "name",
       children: "children",
-      process: "isStudied",
+      process: "study_status",
     }),
   },
   leave: {
@@ -58,25 +58,25 @@ const emit = defineEmits(["onChnage", "onClickButton"]);
           <view
             @click="emit('onClickButton', items)"
             class="comment"
-            v-if="items[keys.process] === 0"
+            v-if="items[keys.process] === 1"
             >开始学习</view
           >
           <view
             @click="emit('onClickButton', items)"
             class="comment warning"
-            v-if="items[keys.process] > 0 && items[keys.process] < 100"
+            v-if="items[keys.process] === 2"
             >继续学习</view
           >
           <view
-            @click="emit('onClickButton', i)"
+            @click="emit('onClickButton', items)"
             class="comment"
-            v-if="items[keys.process] === 100"
+            v-if="items[keys.process] === 4"
             >去练习</view
           >
           <view
             @click="emit('onClickButton', items)"
             class="comment success"
-            v-if="items[keys.process] === 100"
+            v-if="items[keys.process] === 3"
             >已学习</view
           >
         </view>

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 2
pages/challenge/index.vue


+ 349 - 274
pages/index/index.vue

@@ -1,343 +1,418 @@
 <template>
-	<Container class="body-content" title="首页" bgColor="#F8F8F8" :showBack="false">
-		<view class="home">
-			<!-- 倒计时 -->
-			<view class="time" v-if="(splitDays != '') & (splitDays != null)">
-				<view>倒计时</view>
-				<!-- 将天数拆分为单个数字显示 -->
-				<view class="exam-countdown-tiem-item" v-for="(digit, index) in splitDays" :key="index">{{ digit }}</view>
-				<view>天</view>
-			</view>
-			<!-- 轮播图 -->
-			<swiper class="swiper" circular autoplay>
-				<swiper-item v-for="(item, index) in banner_list" :key="index">
-					<view class="swiper-item"><image class="swiper-item-image" :src="item.image_url" mode="aspectFill" @load="onImageLoad($event, item, index)" @click="goto_notice_list(item.link_url)"></image></view>
-				</swiper-item>
-			</swiper>
-			<!-- 公告 -->
-			<view class="notice" v-if="notice_list != '' && notice_list != null">
-				<view class="title-lable">公告</view>
-				<view class="notice-title">{{ notice_list[0]['title'] }}</view>
-				<navigator url="/pages/notice/list">
-					<view class="notice-more">{{ '更多 >' }}</view>
-				</navigator>
-			</view>
-			<!-- 2025新大纲 -->
-			<view class="new_outline">
-				<uni-section title="基础用法" type="line">
-					<view class="p-20">
-						<uni-segmented-control
-							:flex="false"
-							:current="current"
-							:values="list.map((item) => item.name)"
-							style-type="text"
-							@clickItem="(e) => (current = e.currentIndex)"
-						/>
-						<!-- 执业药师 -->
-						<template v-for="(item, index) in list" :key="item.id">
-							<view v-if="current === index" class="grid">
-								<view v-for="i in item?.children" class="flex" @click="clickClass(i)">
-									<image :src="i.chapter_image_url" class="img_small"></image>
-									<view>{{ i.name }}</view>
-								</view>
-							</view>
-						</template>
-					</view>
-				</uni-section>
-			</view>
-			<view class="p-20">
-				<uni-section title="往年真题" type="line">
-					<!-- 往年真题 -->
-					<view class="grid-3">
-						<view
-							v-for="item in 7"
-							class="flex"
-							@click="
-								toReal({
-									title: '2025真题'
-								})
-							"
-						>
-							<view class="bg-red">
-								<view class="text">执业药师{{ item }}</view>
-							</view>
-						</view>
-					</view>
-				</uni-section>
-			</view>
-		</view>
-	</Container>
+  <Container
+    class="body-content"
+    title="首页"
+    bgColor="#F8F8F8"
+    :showBack="false"
+  >
+    <view class="home">
+      <!-- 倒计时 -->
+      <view class="time" v-if="(splitDays != '') & (splitDays != null)">
+        <view>倒计时</view>
+        <!-- 将天数拆分为单个数字显示 -->
+        <view
+          class="exam-countdown-tiem-item"
+          v-for="(digit, index) in splitDays"
+          :key="index"
+          >{{ digit }}</view
+        >
+        <view>天</view>
+      </view>
+      <!-- 轮播图 -->
+      <swiper class="swiper" circular autoplay>
+        <swiper-item v-for="(item, index) in banner_list" :key="index">
+          <view class="swiper-item"
+            ><image
+              class="swiper-item-image"
+              :src="item.image_url"
+              mode="aspectFill"
+              @load="onImageLoad($event, item, index)"
+              @click="goto_notice_list(item.link_url)"
+            ></image
+          ></view>
+        </swiper-item>
+      </swiper>
+      <!-- 公告 -->
+      <view class="notice" v-if="notice_list != '' && notice_list != null">
+        <view class="title-lable">公告</view>
+        <view class="notice-title">{{ notice_list[0]["title"] }}</view>
+        <navigator url="/pages/notice/list">
+          <view class="notice-more">{{ "更多 >" }}</view>
+        </navigator>
+      </view>
+      <!-- 2025新大纲 -->
+      <view class="new_outline">
+        <uni-section title="基础用法" type="line">
+          <view class="p-20">
+            <uni-segmented-control
+              :flex="false"
+              :current="current"
+              :values="list.map((item) => item.name)"
+              style-type="text"
+              @clickItem="(e) => (current = e.currentIndex)"
+            />
+            <!-- 执业药师 -->
+            <template v-for="(item, index) in list" :key="item.id">
+              <view v-if="current === index" class="grid">
+                <view
+                  v-for="i in item?.children"
+                  :key="i.id"
+                  class="flex"
+                  @click="clickClass(i)"
+                >
+                  <image :src="i.chapter_image_url" class="img_small"></image>
+                  <view>{{ i.name }}</view>
+                </view>
+              </view>
+            </template>
+          </view>
+        </uni-section>
+      </view>
+      <view class="p-20">
+        <uni-section title="往年真题" type="line">
+          <!-- 往年真题 -->
+          <view class="grid-3">
+            <view
+              v-for="item in rightList"
+              :key="item.originName"
+              class="flex"
+              @click="
+                toReal({
+                  title: item.name + '真题',
+                  origin: item.originName,
+                })
+              "
+            >
+              <view class="bg-red">
+                <view class="text">{{ item.name }}真题</view>
+              </view>
+            </view>
+          </view>
+        </uni-section>
+      </view>
+    </view>
+  </Container>
 </template>
 
 <script setup>
-import { ref, onMounted, computed, onBeforeUnmount } from 'vue';
+import { ref, onMounted, computed, onBeforeUnmount } from "vue";
 
-import Container from '../../components/Container/Container.vue';
-import { router } from '../../utils/router';
-import { request } from '../../utils/request';
-import { arrayToTree } from '../../utils';
+import Container from "../../components/Container/Container.vue";
+import { router } from "../../utils/router";
+import { request } from "../../utils/request";
+import { arrayToTree } from "../../utils";
 
 const current = ref(0);
 const list = ref([]);
 const banner_list = ref([]);
 const notice_list = ref([]);
-const imgHeight = ref('auto'); // 初始高度
+const imgHeight = ref("auto"); // 初始高度
+const rightList = ref([]);
 
-const days = ref('000'); // 默认值设为100,确保有3位数
+const days = ref("000"); // 默认值设为100,确保有3位数
 let timer = null;
-const exam_time=ref(''); //考试时间
+const exam_time = ref(""); //考试时间
 
 // 将天数拆分为单个数字数组
 const splitDays = computed(() => {
-	// 将数字转为字符串,然后拆分为字符数组
-	const str = days.value.toString();
-	// 如果不足3位数,前面补0(例如5变成["0","0","5"])
-	return str.padStart(3, '0').split('');
+  // 将数字转为字符串,然后拆分为字符数组
+  const str = days.value.toString();
+  // 如果不足3位数,前面补0(例如5变成["0","0","5"])
+  return str.padStart(3, "0").split("");
 });
 
 const calculateDays = () => {
-	// const targetDate = new Date('2025-12-31');
-	const targetDate = new Date(Number(exam_time.value*1000))
-	const currentDate = new Date();
-	const diffTime = targetDate - currentDate;
-	const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
-	days.value = diffDays > 0 ? diffDays : 0;
+  // const targetDate = new Date('2025-12-31');
+  const targetDate = new Date(Number(exam_time.value * 1000));
+  const currentDate = new Date();
+  const diffTime = targetDate - currentDate;
+  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+  days.value = diffDays > 0 ? diffDays : 0;
 };
 
 const clickClass = ({ id }) => {
-	router.push({
-		url: '/pages/regulations/index',
-		params: {
-			id
-		}
-	});
+  router.push({
+    url: "/pages/regulations/index",
+    params: {
+      id,
+    },
+  });
 };
 
 const toReal = (item) => {
-	router.push({
-		url: '/pages/real/index',
-		params: item
-	});
+  router.push({
+    url: "/pages/real/index",
+    params: item,
+  });
 };
 const onImageLoad = (e, item, index) => {
-	const { width, height } = e.detail;
-	const ratio = height / width;
-	const systemInfo = uni.getSystemInfoSync();
-	const imgHeightValue = systemInfo.windowWidth * ratio;
-	imgHeight.value = imgHeightValue + 'px';
+  const { width, height } = e.detail;
+  const ratio = height / width;
+  const systemInfo = uni.getSystemInfoSync();
+  const imgHeightValue = systemInfo.windowWidth * ratio;
+  imgHeight.value = imgHeightValue + "px";
 };
 onBeforeUnmount(() => {
-	if (timer) clearInterval(timer);
+  if (timer) clearInterval(timer);
 });
-const goto_notice_list=(link_url)=>{
-	uni.navigateTo({
-		url:link_url
-	});
+const goto_notice_list = (link_url) => {
+  uni.navigateTo({
+    url: link_url,
+  });
 };
 onMounted(async () => {
-	const res = await request('api/question_bank/question_reception/chapter/get', {}, 'POST');
-	list.value = arrayToTree({
-		list: res.data
-	});
-	// 首页轮播
-	const banner_res = await request('api/question_bank/question_reception/banner/list', { page: 1, limit: 10 }, 'POST');
-	banner_list.value = banner_res.data.data;
-	//系统公告
-	const notice_res = await request('api/question_bank/question_reception/notice/list', { page: 1, limit: 1 }, 'POST');
-	notice_list.value = notice_res.data.data;
-	//获取考试倒计时
-	const examination_res = await request('api/question_bank/question_reception/common_config/detail', { content_code: 'examination_countdown' }, 'POST');
-	exam_time.value=examination_res !='' ? examination_res.data.content_detail:'';
+  const res = await request(
+    "api/question_bank/question_reception/chapter/get",
+    {},
+    "POST"
+  );
+  list.value = arrayToTree({
+    list: res.data,
+  });
+  // 首页轮播
+  const banner_res = await request(
+    "api/question_bank/question_reception/banner/list",
+    { page: 1, limit: 10 },
+    "POST"
+  );
+  banner_list.value = banner_res.data.data;
+  //系统公告
+  const notice_res = await request(
+    "api/question_bank/question_reception/notice/list",
+    { page: 1, limit: 1 },
+    "POST"
+  );
+  notice_list.value = notice_res.data.data;
+  //获取考试倒计时
+  const examination_res = await request(
+    "api/question_bank/question_reception/common_config/detail",
+    { content_code: "examination_countdown" },
+    "POST"
+  );
+  exam_time.value =
+    examination_res != "" ? examination_res.data.content_detail : "";
 
-	calculateDays();
-	timer = setInterval(() => {
-		calculateDays();
-	}, 24 * 60 * 60 * 1000);
-	return {
-		days,
-		splitDays
-	};
+  // 获取历年真题
+  const rightListRes = await request(
+    "api/question_bank/question_reception/real_catalogue/get_year",
+    {},
+    "post"
+  );
+  rightList.value = rightListRes.data.map((i) => {
+    const newItem = {
+      name: i + "年",
+      originName: i,
+    };
+    if (i.includes("(")) {
+      // 2022(1)变成 2022年(一)
+      newItem.name = i.replace(/\((\d+)\)/g, (text, number) => {
+        const chineseNumber = [
+          "一",
+          "二",
+          "三",
+          "四",
+          "五",
+          "六",
+          "七",
+          "八",
+          "九",
+        ];
+        return `年(${chineseNumber[number - 1]})`;
+      });
+    }
+    return newItem;
+  });
+
+  calculateDays();
+  timer = setInterval(() => {
+    calculateDays();
+  }, 24 * 60 * 60 * 1000);
+  return {
+    days,
+    splitDays,
+  };
 });
 </script>
 
 <style scoped lang="scss">
-@import '@/uni.scss';
+@import "@/uni.scss";
 .img_small {
-	width: 112rpx;
-	height: 112rpx;
+  width: 112rpx;
+  height: 112rpx;
 }
 .body-content {
-	background: #f8f8f8;
-	border-radius: 0rpx 0rpx 0rpx 0rpx;
+  background: #f8f8f8;
+  border-radius: 0rpx 0rpx 0rpx 0rpx;
 }
 .exam-countdown-tiem-item {
-	width: 24rpx;
-	height: 34rpx;
-	background: #ff3c3c;
-	border-radius: 2rpx 2rpx 2rpx 2rpx;
-	font-family: PingFang SC, PingFang SC;
-	font-weight: 500;
-	font-size: 24rpx;
-	color: #ffffff;
-	display: flex;
-	align-items: center;
-	justify-content: center;
+  width: 24rpx;
+  height: 34rpx;
+  background: #ff3c3c;
+  border-radius: 2rpx 2rpx 2rpx 2rpx;
+  font-family: PingFang SC, PingFang SC;
+  font-weight: 500;
+  font-size: 24rpx;
+  color: #ffffff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
 }
 .home {
-	display: flex;
-	flex-direction: column;
-	gap: 20rpx;
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
 
-	.time {
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		font-family: PingFang SC, PingFang SC;
-		font-weight: 500;
-		font-size: 24rpx;
-		color: #333333;
-		gap: 8rpx;
+  .time {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-family: PingFang SC, PingFang SC;
+    font-weight: 500;
+    font-size: 24rpx;
+    color: #333333;
+    gap: 8rpx;
 
-		.tiem-item {
-			width: 24rpx;
-			height: 34rpx;
-			background: #ff3c3c;
-			border-radius: 2rpx 2rpx 2rpx 2rpx;
-			font-family: PingFang SC, PingFang SC;
-			font-weight: 500;
-			font-size: 24rpx;
-			color: #ffffff;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-		}
-	}
+    .tiem-item {
+      width: 24rpx;
+      height: 34rpx;
+      background: #ff3c3c;
+      border-radius: 2rpx 2rpx 2rpx 2rpx;
+      font-family: PingFang SC, PingFang SC;
+      font-weight: 500;
+      font-size: 24rpx;
+      color: #ffffff;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
 
-	.swiper {
-		width: 100%;
-		height: v-bind(imgHeight);
-		/* 关键修复样式 */
-		overflow: hidden;
-		transform-style: preserve-3d;
+  .swiper {
+    width: 100%;
+    height: v-bind(imgHeight);
+    /* 关键修复样式 */
+    overflow: hidden;
+    transform-style: preserve-3d;
 
-		.swiper-item {
-			width: 100%;
-			height: 100%;
-			overflow: hidden;
-			border-radius: 30rpx;
-			/* 确保变换不影响子元素 */
-			transform: translateZ(0);
+    .swiper-item {
+      width: 100%;
+      height: 100%;
+      overflow: hidden;
+      border-radius: 30rpx;
+      /* 确保变换不影响子元素 */
+      transform: translateZ(0);
 
-			.swiper-item-image {
-				width: 100%;
-				height: 100%;
-				display: block;
-				border-radius: 30rpx;
-				/* 防止图片变形 */
-				object-fit: cover;
-				/* 确保层级 */
-				position: relative;
-			}
-		}
-	}
+      .swiper-item-image {
+        width: 100%;
+        height: 100%;
+        display: block;
+        border-radius: 30rpx;
+        /* 防止图片变形 */
+        object-fit: cover;
+        /* 确保层级 */
+        position: relative;
+      }
+    }
+  }
 
-	.notice {
-		display: flex;
-		align-items: center;
-		gap: 20rpx;
-		padding-left: 26rpx;
-		height: 72rpx;
-		background: #ffffff;
-		border-radius: 500rpx 500rpx 500rpx 500rpx;
-		border: 1rpx solid #f8f8f8;
-		.title-lable {
-			padding: 5rpx 15rpx;
-			border-radius: 8rpx 8rpx 8rpx 8rpx;
-			border: 1rpx solid #3f75ff;
-			font-weight: 500;
-			font-size: 26rpx;
-			color: #3f75ff;
-			line-height: 32rpx;
-			text-align: center;
-			font-style: normal;
-			text-transform: none;
-		}
-		.notice-title {
-			width: calc(100% - 225rpx);
-			color: #333;
-			font-size: 26rpx;
-			display: -webkit-box;
-			-webkit-box-orient: vertical;
-			-webkit-line-clamp: 1;
-			overflow: hidden;
-			text-overflow: ellipsis;
-		}
-		.notice-more {
-			font-size: 26rpx;
-		}
-	}
+  .notice {
+    display: flex;
+    align-items: center;
+    gap: 20rpx;
+    padding-left: 26rpx;
+    height: 72rpx;
+    background: #ffffff;
+    border-radius: 500rpx 500rpx 500rpx 500rpx;
+    border: 1rpx solid #f8f8f8;
+    .title-lable {
+      padding: 5rpx 15rpx;
+      border-radius: 8rpx 8rpx 8rpx 8rpx;
+      border: 1rpx solid #3f75ff;
+      font-weight: 500;
+      font-size: 26rpx;
+      color: #3f75ff;
+      line-height: 32rpx;
+      text-align: center;
+      font-style: normal;
+      text-transform: none;
+    }
+    .notice-title {
+      width: calc(100% - 225rpx);
+      color: #333;
+      font-size: 26rpx;
+      display: -webkit-box;
+      -webkit-box-orient: vertical;
+      -webkit-line-clamp: 1;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    .notice-more {
+      font-size: 26rpx;
+    }
+  }
 }
 
 .title {
-	font-family: 'PingFang SC, PingFang SC';
-	font-weight: 700;
-	font-size: 32rpx;
-	color: #000000;
+  font-family: "PingFang SC, PingFang SC";
+  font-weight: 700;
+  font-size: 32rpx;
+  color: #000000;
 }
 
 .p-20 {
-	display: flex;
-	flex-direction: column;
-	gap: 20rpx;
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
 }
 
 .grid {
-	display: grid;
-	grid-template-columns: repeat(4, 1fr);
-	background-color: #ffffff;
-	padding: 24rpx;
-	gap: 16rpx;
-	border-radius: 16rpx;
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  background-color: #ffffff;
+  padding: 24rpx;
+  gap: 16rpx;
+  border-radius: 16rpx;
 }
 
 .grid-3 {
-	display: grid;
-	grid-template-columns: repeat(3, 1fr);
-	background-color: #ffffff;
-	padding: 24rpx;
-	gap: 16rpx;
-	border-radius: 16rpx;
-	height: 100%;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  background-color: #ffffff;
+  padding: 24rpx;
+  gap: 16rpx;
+  border-radius: 16rpx;
+  height: 100%;
 }
 
 .flex {
-	display: flex;
-	flex-direction: column;
-	gap: 20rpx;
-	align-items: center;
-	justify-content: center;
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+  align-items: center;
+  justify-content: center;
 }
 
 .bg-red {
-	width: 191.07rpx;
-	height: 179.61rpx;
-	background: url('https://openwork-oss.oss-cn-shenzhen.aliyuncs.com/uploads/question/2025/05/WmhlbORF2q8A62Ytg1RVac8AYSGPkf7F2pEY6jQP.png') no-repeat;
-	background-size: cover;
-	display: flex;
-	justify-content: center;
+  width: 191.07rpx;
+  height: 179.61rpx;
+  background: url("https://openwork-oss.oss-cn-shenzhen.aliyuncs.com/uploads/question/2025/05/WmhlbORF2q8A62Ytg1RVac8AYSGPkf7F2pEY6jQP.png")
+    no-repeat;
+  background-size: cover;
+  display: flex;
+  justify-content: center;
 
-	.text {
-		font-family: jiangxizhuokai, jiangxizhuokai;
-		font-weight: bold;
-		font-size: 27rpx;
-		color: #3f75ff;
-		text-shadow: 0px 2px 4px #bdcfff;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-		margin-top: 16rpx;
-		width: 95rpx;
-		height: 70rpx;
-	}
+  .text {
+    font-family: jiangxizhuokai, jiangxizhuokai;
+    font-weight: bold;
+    font-size: 27rpx;
+    color: #3f75ff;
+    text-shadow: 0px 2px 4px #bdcfff;
+    text-align: left;
+    font-style: normal;
+    text-transform: none;
+    margin-top: 16rpx;
+    width: 95rpx;
+    height: 70rpx;
+  }
 }
-</style>
+</style>

+ 15 - 16
pages/real/exam.vue

@@ -1,10 +1,11 @@
 <script setup name="Exam">
 import TopicExam from "../../components/Topic/TopicExam.vue";
 import { ref, onMounted } from "vue";
-import { getRoute, router } from "../../utils/router";
+import { getRoute } from "../../utils/router";
 import { request } from "../../utils/request";
 import { useTimeStore } from "@/store/time";
 import Container from "../../components/Container/Container.vue";
+import ShareTemplate from "../../components/ShareTemplate/ShareTemplate.vue";
 
 const Time = useTimeStore();
 
@@ -17,9 +18,11 @@ const correct = ref({
   error: 0, // 错误数
   right: 0, // 正确数
   not: 0, // 未答题
+  total: 0, // 总题数
 });
 
 const showReport = ref(false);
+const showShare = ref(false);
 const submitter = ref({
   closeText: "直接退出",
   context: "您本次答题还未提交, 确定要退出答题吗?",
@@ -108,6 +111,7 @@ const lookReport = (d, s) => {
     right: r,
     error: total.value - r - n,
     not: n,
+    total: total.value,
   };
 };
 
@@ -129,12 +133,12 @@ onMounted(() => {
     @lookReport="lookReport"
     :empty="!data.length"
     border
-    v-if="!showReport"
+    v-if="!showReport && !showShare"
   />
   <Container
     bgColor="url('https://openwork-oss.oss-cn-shenzhen.aliyuncs.com/uploads/question/2025/05/7i6ROn34tZGe8QR4gaWDP1ZzyPYjqlvPgLLFRmIe.png')"
     bottomBorder
-    v-else
+    v-else-if="!showShare && showReport"
     bottomBgColor="#fff"
     title="练习报告"
   >
@@ -180,21 +184,16 @@ onMounted(() => {
     <template #footer>
       <view class="footer">
         <view class="button plain" @click="showReport = false">答题解析</view>
-        <view
-          class="button"
-          @click="
-            router.push({
-              url: '/pages/real/sharePractice',
-              params: {
-                correct,
-              },
-            })
-          "
-          >炫耀一下</view
-        >
+        <view class="button" @click="showShare = true">炫耀一下</view>
       </view>
     </template>
   </Container>
+  <ShareTemplate
+    :onClose="() => (showShare = false)"
+    :correct="correct"
+    bg="https://openwork-oss.oss-cn-shenzhen.aliyuncs.com/uploads/question/2025/05/ix4wGk9h7Fb4c3d8hjkYjJP6VsPNbZgG3uDTUzxp.png"
+    v-else
+  />
 </template>
 
 <style lang="scss" scoped>
@@ -214,7 +213,7 @@ onMounted(() => {
   justify-content: center;
   font-weight: 500;
   font-size: 50rpx;
-  color: #002FA7;
+  color: #002fa7;
 }
 .card {
   display: flex;

+ 1 - 1
pages/real/index.vue

@@ -12,7 +12,7 @@
       <!-- 执业药师 -->
       <template v-for="(item, index) in list" :key="item.id">
         <view v-if="current === index" class="grid">
-          <view v-for="i in item?.children" class="flex">
+          <view v-for="i in item?.children" :key="i.id" class="flex">
             <view class="bg-red">
               <view class="text">{{ i.name }}</view>
               <view class="button" @click="clickClass(i)">开始考试</view>

+ 20 - 38
pages/regulations/index.vue

@@ -13,9 +13,9 @@
     >
       <view class="free-content">
         <uni-collapse-item
-          :open="index <= 1"
+          open
           :title="item.name"
-          v-for="(item, index) in chaptersList.slice(0, 2)"
+          v-for="item in freeChaptersList"
           :key="item.id"
           class="text"
         >
@@ -29,7 +29,7 @@
         </uni-collapse-item>
       </view>
       <!-- 付费功能 -->
-      <view class="pay-content" v-if="chaptersList.length > 2">
+      <view class="pay-content" v-if="chaptersList.length">
         <view
           class="modal-mask"
           :style="{
@@ -43,40 +43,16 @@
         </view>
         <uni-collapse-item
           :title="item.name"
-          v-for="item in chaptersList.slice(2)"
+          v-for="item in chaptersList"
           :key="item.id"
           class="text"
         >
           <view class="-mt-20">
-            <view class="content" v-for="i in item.children" :key="i.id">
-              <view class="text"> {{ i.name }}</view>
-              <view class="buttons">
-                <view
-                  @click="onClickButton(i)"
-                  class="comment"
-                  v-if="i.isStudied === 0"
-                  >开始学习</view
-                >
-                <view
-                  @click="onClickButton(i)"
-                  class="comment warning"
-                  v-if="i.isStudied > 0 && i.isStudied < 100"
-                  >继续学习</view
-                >
-                <view
-                  @click="onClickButton(i)"
-                  class="comment"
-                  v-if="i.isStudied === 100"
-                  >去练习</view
-                >
-                <view
-                  class="comment success"
-                  @click="onClickButton(i)"
-                  v-if="i.isStudied === 100"
-                  >已学习</view
-                >
-              </view>
-            </view>
+            <Tree
+              :chaptersList="item.children"
+              @onChnage="onChnage"
+              @onClickButton="onClickButton"
+            />
           </view>
         </uni-collapse-item>
       </view>
@@ -85,7 +61,6 @@
       ref="popup"
       background-color="#fff"
       v-model:open="showContainer"
-      @change="onChange"
       title="邀请新客户解锁课程"
     >
       <text class="unlock"
@@ -118,6 +93,7 @@ const maskStyle = ref({
 });
 
 const chaptersList = ref([]);
+const freeChaptersList = ref([]);
 
 // 点击学习按钮
 const onClickButton = (item) => {
@@ -126,7 +102,7 @@ const onClickButton = (item) => {
     params: {
       id: item.id,
       parent_id: item.parent_id,
-      name: item.name
+      name: item.name,
     },
   });
 };
@@ -151,7 +127,7 @@ const onSafeAreaChange = (s) => {
 
 const onChnage = () => {
   resolveHeight();
-}
+};
 
 const onClickMask = () => {
   showContainer.value = true;
@@ -165,10 +141,16 @@ onMounted(async () => {
       id,
     }
   );
-  chaptersList.value = arrayToTree({
-    list: res.data.map((item) => ({ isStudied: 0, ...item })),
+  const list = arrayToTree({
+    list: res.data,
     firstId: +id,
   });
+  const freeList = list.filter((item) => !item.is_lock);
+  const payList = list.filter((item) => item.is_lock);
+  // 免费
+  freeChaptersList.value = freeList;
+  // 付费章节
+  chaptersList.value = payList;
 });
 </script>
 

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 3
pages/regulations/learing.vue


+ 0 - 1
pages/user/index.vue

@@ -175,7 +175,6 @@ export default {
 				uni.setStorageSync('userInfo', callback.data);
 			}
 		});
-		this._getPacketNum();
 	},
 	methods: {
 		updateWindowHeight() {

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff