Jelajahi Sumber

接口引入

wyling 3 tahun lalu
induk
melakukan
369de6e9c7

+ 49 - 0
package-lock.json

@@ -77,6 +77,12 @@
       "integrity": "sha1-GNyAkbKF35DbLyWqfZBs/DlLf3Q=",
       "dev": true
     },
+    "@types/howler": {
+      "version": "2.2.4",
+      "resolved": "https://registry.nlark.com/@types/howler/download/@types/howler-2.2.4.tgz",
+      "integrity": "sha1-+30CvDNdIMwUGYfxbBlbGvd9dWo=",
+      "dev": true
+    },
     "@types/marked": {
       "version": "3.0.1",
       "resolved": "https://registry.nlark.com/@types/marked/download/@types/marked-3.0.1.tgz?cache=0&sync_timestamp=1631543903416&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fmarked%2Fdownload%2F%40types%2Fmarked-3.0.1.tgz",
@@ -89,6 +95,12 @@
       "integrity": "sha1-5waVHV4ztPCku3Ox+LEk4m8IHeA=",
       "dev": true
     },
+    "@types/node": {
+      "version": "16.11.6",
+      "resolved": "https://registry.npmmirror.com/@types/node/download/@types/node-16.11.6.tgz?cache=0&sync_timestamp=1635213425908&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-16.11.6.tgz",
+      "integrity": "sha1-a+96KgrWhM9ukPz+Mc7KvZzgo64=",
+      "dev": true
+    },
     "@types/preloadjs": {
       "version": "0.6.32",
       "resolved": "https://registry.nlark.com/@types/preloadjs/download/@types/preloadjs-0.6.32.tgz",
@@ -443,6 +455,11 @@
       "resolved": "https://registry.nlark.com/csstype/download/csstype-2.6.17.tgz",
       "integrity": "sha1-TPMOuH4dGgBdi2UQ+VKSQT9qHA4="
     },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz",
+      "integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU="
+    },
     "doctypes": {
       "version": "1.1.0",
       "resolved": "https://registry.npm.taobao.org/doctypes/download/doctypes-1.1.0.tgz",
@@ -596,6 +613,11 @@
       "integrity": "sha1-gdAbtd6OpKIUrV1urRtSNGCwtFo=",
       "dev": true
     },
+    "howler": {
+      "version": "2.2.3",
+      "resolved": "https://registry.nlark.com/howler/download/howler-2.2.3.tgz",
+      "integrity": "sha1-ou/5sItYZ5jnou4XpgKpDfKHFdo="
+    },
     "htmlparser2": {
       "version": "6.1.0",
       "resolved": "https://registry.nlark.com/htmlparser2/download/htmlparser2-6.1.0.tgz",
@@ -1074,6 +1096,11 @@
         "chokidar": ">=3.0.0 <4.0.0"
       }
     },
+    "shvl": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npm.taobao.org/shvl/download/shvl-2.0.3.tgz",
+      "integrity": "sha1-60vTdkT1aEu6H8UsMBDJb7Xmr9E="
+    },
     "soundjs": {
       "version": "1.0.1",
       "resolved": "https://registry.nlark.com/soundjs/download/soundjs-1.0.1.tgz",
@@ -1515,6 +1542,28 @@
         "@vue/devtools-api": "^6.0.0-beta.11"
       }
     },
+    "vuex-persistedstate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.nlark.com/vuex-persistedstate/download/vuex-persistedstate-4.1.0.tgz?cache=0&sync_timestamp=1632225657120&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvuex-persistedstate%2Fdownload%2Fvuex-persistedstate-4.1.0.tgz",
+      "integrity": "sha1-EnFl+F9bRTT7MXCl06i+mBG9KlM=",
+      "requires": {
+        "deepmerge": "^4.2.2",
+        "shvl": "^2.0.3"
+      }
+    },
+    "weixin-js-sdk": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npm.taobao.org/weixin-js-sdk/download/weixin-js-sdk-1.6.0.tgz",
+      "integrity": "sha1-/1BITYEYzhII8RJIz0ocCDFXdRQ="
+    },
+    "weixin-js-sdk-ts": {
+      "version": "1.6.1",
+      "resolved": "https://registry.nlark.com/weixin-js-sdk-ts/download/weixin-js-sdk-ts-1.6.1.tgz",
+      "integrity": "sha1-7lh0s5ZiQrWrS+H9WvyQ7FUzU80=",
+      "requires": {
+        "weixin-js-sdk": "^1.6.0"
+      }
+    },
     "with": {
       "version": "7.0.2",
       "resolved": "https://registry.npm.taobao.org/with/download/with-7.0.2.tgz",

+ 7 - 2
package.json

@@ -7,6 +7,8 @@
     "serve": "vite preview"
   },
   "dependencies": {
+    "axios": "^0.21.1",
+    "howler": "^2.2.3",
     "marked": "^3.0.4",
     "mockjs": "^1.1.0",
     "soundjs": "^1.0.1",
@@ -14,12 +16,15 @@
     "vconsole": "^3.9.1",
     "vue": "^3.0.5",
     "vue-router": "^4.0.10",
-    "axios": "^0.21.1",
-    "vuex": "^4.0.2"
+    "vuex": "^4.0.2",
+    "vuex-persistedstate": "^4.1.0",
+    "weixin-js-sdk-ts": "^1.6.1"
   },
   "devDependencies": {
+    "@types/howler": "^2.2.4",
     "@types/marked": "^3.0.1",
     "@types/mockjs": "^1.0.4",
+    "@types/node": "^16.11.6",
     "@types/soundjs": "^0.6.28",
     "@vitejs/plugin-vue": "^1.3.0",
     "@vue/compiler-sfc": "^3.0.5",

+ 3 - 1
src/api/index.ts

@@ -1,3 +1,5 @@
 export * from "./modules/home";
 export * from "./modules/test";
-export * from "./modules/auth"
+export * from "./modules/auth";
+export * from "./modules/pay";
+export * from "./modules/testScores";

+ 25 - 2
src/api/modules/auth.ts

@@ -1,16 +1,39 @@
 import request from "../request";
+import { LocationQueryValue } from "vue-router";
+
+interface loginRes {
+  code: number;
+  msg: string;
+  data: {
+    token: string;
+    wxUserInfo: {
+      nickname: string;
+      headimgurl: string;
+    };
+  };
+}
 
 /**
  *
  * @param code 微信code
  * @returns
  */
-export function login(code: string) {
-  return request({
+export async function login(code: LocationQueryValue | LocationQueryValue[]) {
+  let res = await request({
     url: "/twzd-admin/login/code/test",
     method: "post",
+    headers: {
+      isToken: false,
+    },
     params: {
       authorizationCode: code,
     },
   });
+  return <loginRes>res.data;
+}
+
+export async function userInfo() {
+  return request({
+    url: "/twzd-admin/student/user/info",
+  });
 }

+ 76 - 0
src/api/modules/pay.ts

@@ -0,0 +1,76 @@
+import request from "../request";
+import * as API from "@/api";
+import store from "@/store";
+
+declare let WeixinJSBridge: any;
+
+interface PayConfig {
+  appId: string;
+  timeStamp: string;
+  nonceStr: string;
+  package: string;
+  signType: string;
+  paySign: string;
+}
+
+function onBridgeReady(payConfig: PayConfig) {
+  WeixinJSBridge.invoke(
+    "getBrandWCPayRequest",
+    payConfig,
+    async function (res: any) {
+      if (res.err_msg == "get_brand_wcpay_request:ok") {
+        // 使用以上方式判断前端返回,微信团队郑重提示:
+        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+        console.log("支付成功");
+        let userDataRes = await API.userInfo();
+        store.commit("setUserData", {
+          ...store.getters.getUserData,
+          expireTime: userDataRes.data.data.expireTime,
+        });
+      }
+    }
+  );
+}
+
+/**
+ * 获取支付配置,并调起微信支付
+ */
+export async function prepareOrder(dictCode: number) {
+  let res = await request({
+    url: "/twzd-admin/student/wx/prepareOrder",
+    method: "post",
+    data: {
+      dictCode,
+    },
+  });
+
+  if (res.data.code === 200) {
+    if (typeof WeixinJSBridge !== "undefined") {
+      onBridgeReady(res.data.data);
+    }
+  }
+}
+
+/**
+ * 获取支付配置,并调起微信支付
+ */
+export async function LoopPrepareOrder() {
+  // let res = await request({
+  //   url: "/twzd-admin/student/wx/prepareOrder-test",
+  //   method: "post",
+  // });
+
+  let res = await request({
+    url: "/twzd-admin/student/wx/prepareOrder",
+    method: "post",
+    data: {
+      dictCode: 33,
+    },
+  });
+
+  if (res.data.code === 200) {
+    if (typeof WeixinJSBridge !== "undefined") {
+      onBridgeReady(res.data.data);
+    }
+  }
+}

+ 54 - 20
src/api/modules/test.ts

@@ -1,13 +1,20 @@
 import request from "../request";
 import Mcok from "mockjs";
 
+interface topicQuery {
+  //分类练习
+  classIssue: string;
+  //当前页码
+  pageNum: string;
+  //每页数据量
+  pageSize: string;
+}
+
 /**
  * 获取题目列表
  * @returns
  */
-export function getTopicList() {
-  const type = Mcok.Random.natural(0, 2);
-
+export async function getTopicList(params: any) {
   const getOpts = (type: Number) => {
     switch (type) {
       case 0:
@@ -41,23 +48,50 @@ export function getTopicList() {
     }
   };
 
-  const data = Mcok.mock({
-    code: 200,
-    msg: "请求成功",
-    data: {
-      total: 1000,
-      "list|15": [
-        {
-          explain: "@cparagraph(1, 3)",
-          opts: getOpts(type),
-          type,
-          answer: getAnswer(type),
-          userAnswer: getUserAnswer(type),
-          isTrue: null,
-        },
-      ],
-    },
+  const getType = (answer: Array<string>) => {
+    if (answer.length === 1) {
+      if (["√", "×"].includes(answer[0])) {
+        return 0;
+      } else {
+        return 1;
+      }
+    } else {
+      return 2;
+    }
+  };
+
+  let res = await request({
+    url: "/twzd-admin/qustion/info/list",
+    params,
   });
 
-  return data;
+  let data2 = {
+    total: res.data.total,
+    list: res.data.rows.map((item: any) => {
+      return {
+        ...item,
+        explain: item.issue,
+        opts: item.opts.split("-"),
+        image: item.image,
+        type: getType(item.answer.split("-")),
+        answer: item.answer,
+        userAnswer: getUserAnswer(getType(item.answer.split("-"))),
+        isTrue: null,
+      };
+    }),
+  };
+
+  console.log(res.data);
+
+  return data2;
+}
+
+/**
+ * 获取题目分类
+ */
+export async function getTopicClass(path: string, params: any) {
+  return request({
+    url: `/twzd-admin/qustion/info/${path}`,
+    params,
+  });
 }

+ 25 - 0
src/api/modules/testScores.ts

@@ -0,0 +1,25 @@
+import request from "../request";
+
+/**
+ * 查询模拟考成绩列表
+ * @returns 
+ */
+export const getTestScoresList = async () => {
+  let res = await request({
+    url: "/twzd-admin/score/info/list",
+  });
+
+  return res.data;
+};
+
+/**
+ * 获取最大成绩,平均成绩,预测成绩
+ * @returns 
+ */
+export const getTestScoresInfo = async () => {
+    let res = await request({
+      url: "/twzd-admin/score/info/getScoreInfoAll",
+    });
+  
+    return res.data;
+  };

+ 15 - 5
src/api/request.ts

@@ -1,7 +1,17 @@
-import axios from 'axios'
+import axios from "axios";
+import store from "@/store";
 
-const request=axios.create({
-    baseURL:'http://192.168.8.213:8080'
-})
+const request = axios.create({
+  baseURL: "/api",
+});
 
-export default request
+request.interceptors.request.use((config) => {
+  // 是否需要设置 token
+  if (config.headers.isToken !== false) {
+    config.headers["Authorization"] = "Bearer " + store.getters.getToken; // 让每个请求携带自定义token 请根据实际情况自行修改
+  }
+  return config;
+});
+
+
+export default request;

+ 3 - 1
src/components/m-nav-bar/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="header">
+  <div class="header" :style="transparent ? '' : 'background-color: #ffffff;'">
     <m-icon type="fanhui" @click="onClickLeft" />
     <span class="title">{{ title }}</span>
   </div>
@@ -9,6 +9,7 @@
 import { useRouter } from "vue-router";
 const props = defineProps({
   title: String,
+  transparent: Boolean,
 });
 const router = useRouter();
 const onClickLeft = () => {
@@ -22,6 +23,7 @@ const onClickLeft = () => {
   top: 0;
   font-size: 15px;
   padding: 15px;
+  z-index: 999;
   .title {
     position: absolute;
     left: 50%;

+ 8 - 6
src/components/m-user-avatar/index.vue

@@ -3,14 +3,16 @@
 </template>
 
 <script setup lang="ts">
-import {ref} from "vue"
-const userAvater = ref('https://img.yzcdn.cn/vant/cat.jpeg')
+import { computed } from "vue";
+import { useStore } from "vuex";
+const store = useStore();
+const userAvater = computed(() => store.getters.getUserData.headimgurl);
 </script>
 
 <style scoped>
-.user-avatar{
-    width: 54px;
-    height: 54px;
-    border-radius: 50%;
+.user-avatar {
+  width: 54px;
+  height: 54px;
+  border-radius: 50%;
 }
 </style>

+ 6 - 5
src/components/m-user-name/index.vue

@@ -1,11 +1,12 @@
 <template>
-  <span class="user-name">{{userName}}</span>
+  <span class="user-name">{{ userName }}</span>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
-const userName = ref('我有六把刀')
+import { computed } from "vue";
+import { useStore } from "vuex";
+const store = useStore();
+const userName = computed(() => store.getters.getUserData.nickname);
 </script>
 
-<style scoped>
-</style>
+<style scoped></style>

+ 1 - 1
src/main.ts

@@ -8,7 +8,7 @@ import './utils/rem'
 import components from './components'
 
 import VConsole from "vconsole";
-// new VConsole()
+new VConsole()
 
 const app = createApp(App);
 app.use(store);

+ 17 - 3
src/store/index.ts

@@ -1,12 +1,26 @@
 import { createStore } from "vuex";
+import createPersistedState from "vuex-persistedstate";
 
 const store = createStore({
+  plugins: [createPersistedState()],
   state: () => ({
-    count: 10,
+    token: "",
+    userData: {},
   }),
+  getters: {
+    getToken(state) {
+      return state.token;
+    },
+    getUserData(state) {
+      return state.userData;
+    },
+  },
   mutations: {
-    increment(state) {
-      state.count++;
+    setToken(state, token: string) {
+      state.token = token;
+    },
+    setUserData(state, userData: object) {
+      state.userData = userData;
     },
   },
 });

+ 58 - 0
src/views/buyVip/index.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="login-box">
+    <!-- 登陆中 -->
+    <div v-if="loginState == 0">
+      <van-empty description="登陆中"></van-empty>
+    </div>
+    <!-- 登陆成功 -->
+    <div v-else-if="loginState == 1">
+      <van-empty description="登陆成功"></van-empty>
+    </div>
+    <!-- 登陆失败 -->
+    <div v-else-if="loginState == 2">
+      <van-empty image="network" description="登录失败"></van-empty>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+export default defineComponent({
+  name: "login",
+});
+</script>
+
+<script lang="ts" setup>
+import { useRoute, useRouter } from "vue-router";
+import * as API from "@/api";
+import { ref } from "vue";
+import { useStore } from "vuex";
+const router = useRouter();
+const route = useRoute();
+const store = useStore();
+const loginState = ref(0);
+
+API.login(route.query.code).then(async (res) => {
+  if (res.code == 200) {
+    window.localStorage.setItem("token", res.data.token);
+    window.localStorage.setItem(
+      "userData",
+      JSON.stringify(res.data.wxUserInfo)
+    );
+    let userDataRes = await API.userInfo();
+    window.localStorage.setItem("expireTime", userDataRes.data.data.expireTime);
+    loginState.value = 1;
+    router.push("/home/test");
+  } else {
+    loginState.value = 2;
+  }
+});
+</script>
+
+<style scoped lang="scss">
+.login-box {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 32 - 3
src/views/classify/index.vue

@@ -3,12 +3,25 @@
   <div class="cell-box">
     <van-cell
       class="cell"
-      title="单元格"
+      :title="
+        item.placeIssueName || item.classIssueName || item.excellIssueName
+      "
       is-link
       center
-      v-for="(item, index) in 11"
+      v-for="(item, index) in classData"
       :key="index"
       :border="false"
+      @click="
+        router.push({
+          name: 'exercise',
+          query: {
+            ...route.query,
+            placeIssue: item.placeIssue,
+            classIssue: item.classIssue,
+            excellIssue: item.excellIssue,
+          },
+        })
+      "
     >
       <template #icon>
         <div class="icon">{{ index + 1 }}</div>
@@ -17,7 +30,23 @@
   </div>
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { useRoute, useRouter } from "vue-router";
+import { getTopicClass } from "@/api";
+import { ref } from "vue";
+const route = useRoute();
+const router = useRouter();
+const classData = ref();
+getTopicClass(route.query.path as string, route.query).then(({ data }) => {
+  // if(data[0].excellIssueName){
+  //   data=data.map((item:any)=>{
+  //     item.excellIssue
+  //   })
+  // }
+  data.push(data.shift())
+  classData.value = data;
+});
+</script>
 
 <style lang="scss" scoped>
 .cell-box {

+ 47 - 46
src/views/exercise/hooks.ts

@@ -1,5 +1,6 @@
 import { ref, watch, onBeforeMount, Ref, computed } from "vue";
 import * as API from "@/api";
+import { Howl, Howler } from "howler";
 
 //答题模式切换
 export function useTopicMode() {
@@ -39,50 +40,42 @@ export function useTopicMode() {
 //语音设置
 export function useAudioSet(currentAnswerIndex: Ref<number>) {
   const aotuPlayFlag = ref(false);
-  //音频模块
-  const audio = ref();
 
+  let sound: Howl;
   /**
-   * 读题
+   * 播放音频
+   * @param audioUrl
    */
-  const issueAudioPlay = () => {
-    audio.value.src =
-      "https://t1-1305573081.file.myqcloud.com/issue/issue1.mp3";
-    audio.value.play();
-  };
-
-  /**
-   * 读答案
-   */
-  const answerAudioPlay = () => {
-    audio.value.src =
-      "https://t1-1305573081.file.myqcloud.com/answer/answer1.mp3";
-    audio.value.play();
-  };
-
-  /**
-   * 读官方解释
-   */
-  const explainAudioPlay = () => {
-    audio.value.src =
-      "https://t1-1305573081.file.myqcloud.com/explainjs/explainJS1.mp3";
-    audio.value.play();
+  const audioPlay = (audioUrl: string | string[]) => {
+    audioPause();
+    sound = new Howl({
+      src: audioUrl,
+    });
+    sound.once("load", function () {
+      sound.play();
+    });
+    if (typeof audioUrl === "object") {
+      sound.once("end", () => {
+        sound = new Howl({
+          src: audioUrl[1],
+        });
+        sound.once("load", function () {
+          sound.play();
+        });
+      });
+    }
   };
 
   /**
-   * 读技巧解释
+   * 自动读题
    */
-  const JQexplainAudioPlay = () => {
-    audio.value.src =
-      "https://t1-1305573081.file.myqcloud.com/mp3/explain1.mp3";
-    audio.value.play();
-  };
+  const issueAudioPlay = () => {};
 
   /**
    * 停止播放
    */
   const audioPause = () => {
-    audio.value.pause();
+    sound && sound.pause();
   };
 
   //音频模块end
@@ -100,13 +93,10 @@ export function useAudioSet(currentAnswerIndex: Ref<number>) {
 
   return {
     aotuPlayFlag,
-    audio,
     issueAudioPlay,
-    answerAudioPlay,
-    explainAudioPlay,
-    JQexplainAudioPlay,
     audioPause,
     aotuPlaySet,
+    audioPlay,
   };
 }
 
@@ -116,24 +106,35 @@ export function useTopicShow(
   falseNum: Ref<number>,
   idIndex: Ref<number>,
   total: Ref<number>,
-  isJump: Ref<boolean>
+  isJump: Ref<boolean>,
+  query: any
 ) {
   const topicList = ref([]);
 
   //API请求接口数据
   onBeforeMount(async () => {
-    const topicListApiRes = API.getTopicList();
-    setTimeout(() => {
-      topicList.value = topicListApiRes.data.list;
-      idIndex.value = 200;
-      total.value = 1999;
-    }, 1000);
+    const topicListApiRes = await API.getTopicList({
+      ...query,
+      pageNum: 1,
+      pageSize: 10,
+    });
+    topicList.value = topicListApiRes.list;
+    idIndex.value = currentAnswerIndex.value;
+    total.value = topicListApiRes.total;
   });
 
   //当前题目数据
   const currentAnswerIndex = ref(0);
   onBeforeMount(() => {
-    watch(currentAnswerIndex, () => {
+    watch(currentAnswerIndex, async () => {
+      if (currentAnswerIndex.value === topicList.value.length - 1) {
+        const topicListApiRes = await API.getTopicList({
+          ...query,
+          pageNum: 1,
+          pageSize: 10,
+        });
+        topicList.value = topicList.value.concat(topicListApiRes.list);
+      }
       idIndex.value = currentAnswerIndex.value;
     });
   });
@@ -205,6 +206,6 @@ export function useTopicShow(
     currentAnswerIndexBack,
     currentAnswerIndexGo,
     userAnswerChange,
-    topicType
-  }
+    topicType,
+  };
 }

+ 18 - 32
src/views/exercise/index.vue

@@ -37,10 +37,7 @@
     <div class="problem">
       <span class="type">{{ topicType(currentAnswer.type) }}</span>
       <span class="text">{{ currentAnswer.explain }}</span>
-      <img
-        src="https://t1-1305573081.file.myqcloud.com/image/5.jpg"
-        class="img"
-      />
+      <img v-if="currentAnswer.image" :src="currentAnswer.image" class="img" />
     </div>
     <!-- 背题模式展示 -->
     <div v-if="typeParams.answerShow">
@@ -50,9 +47,7 @@
           v-for="(item, index) in currentAnswer.opts"
           :key="Number(index)"
         >
-          <div
-            class="choose-icon"
-          >
+          <div class="choose-icon">
             {{ String.fromCharCode(65 + Number(index)) }}
           </div>
           <span
@@ -166,11 +161,14 @@
       <m-icon type="shoucanghui" size="25px" />
       <span>收藏</span>
     </div>
-    <div class="function-item" @click="answerAudioPlay">
+    <div
+      class="function-item"
+      @click="audioPlay([currentAnswer.issuemp3, currentAnswer.answermp3])"
+    >
       <m-icon type="a-dtda" size="25px" />
       <span>读题+答案</span>
     </div>
-    <div class="function-item" @click="issueAudioPlay">
+    <div class="function-item" @click="audioPlay(currentAnswer.issuemp3)">
       <m-icon type="duti" size="25px" />
       <span>读题</span>
     </div>
@@ -185,15 +183,12 @@
     <div class="skills-box" @click.stop>
       <div class="skills">
         <div class="title">技巧讲解</div>
-        <img
-          src="https://t1-1305573081.file.myqcloud.com/gif/2.gif"
-          class="img"
-        />
+        <img :src="currentAnswer.explainGif" class="img" />
         <van-divider class="divider">本题速记口诀</van-divider>
-        <div class="text">题目以“拘役”结尾.答对;以“徒刑”结尾.答错</div>
+        <div class="text">{{ currentAnswer.explainJq }}</div>
         <div class="btn">
           <span @click="skillsShow = false">关闭</span>
-          <span @click="JQexplainAudioPlay">语音重播</span>
+          <span @click="audioPlay(currentAnswer.explainMp3)">语音重播</span>
         </div>
       </div>
     </div>
@@ -205,19 +200,16 @@
       <div class="skills">
         <div class="title">官方解释</div>
         <div class="text">
-          1、申请城市公交车、大型货车、无轨电车或者有轨电车准驾车型的,在20周岁以上,50周岁以下;2、申请大型客车准驾车型的,在26周岁以上,50周岁以下;3、申请中型客车准驾车型的,在21周岁以上,50周岁以下;4、申请牵引车准驾车型的,在24周岁以上,50周岁以下。
+          {{ currentAnswer.explainJs }}
         </div>
         <div class="btn">
           <span @click="officialShow = false">关闭</span>
-          <span @click="explainAudioPlay">语音重播</span>
+          <span @click="audioPlay(currentAnswer.explainjsmp3)">语音重播</span>
         </div>
       </div>
     </div>
   </van-overlay>
   <!-- 官方解释end -->
-  <!-- 音频模块 -->
-  <audio ref="audio"></audio>
-  <!-- 音频模块end -->
   <!-- 题目模块end -->
   <!-- 设置操作栏 -->
   <van-popup v-model:show="setShow" position="bottom">
@@ -277,7 +269,7 @@
 
 <script lang="ts" setup>
 import * as Api from "@/api";
-import { useRouter } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
 import { ref, watch, computed, reactive, onBeforeMount, nextTick } from "vue";
 import { useTopicMode, useAudioSet, useTopicShow } from "./hooks";
 const router = useRouter();
@@ -285,6 +277,8 @@ const onClickLeft = () => {
   router.back();
 };
 
+const route = useRoute();
+
 //答题模式选择逻辑
 const { answerTypeList, currentType, typeParams } = useTopicMode();
 
@@ -314,19 +308,11 @@ const {
   currentAnswerIndexBack,
   currentAnswerIndexGo,
   userAnswerChange,
-} = useTopicShow(trueNum, falseNum, idIndex, total, isJump);
+} = useTopicShow(trueNum, falseNum, idIndex, total, isJump, route.query);
 
 //音频模块
-const {
-  aotuPlayFlag,
-  audio,
-  answerAudioPlay,
-  audioPause,
-  aotuPlaySet,
-  issueAudioPlay,
-  explainAudioPlay,
-  JQexplainAudioPlay,
-} = useAudioSet(currentAnswerIndex);
+const { aotuPlayFlag, audioPlay, audioPause, aotuPlaySet, issueAudioPlay } =
+  useAudioSet(currentAnswerIndex);
 </script>
 
 <style lang="scss" scoped>

+ 0 - 29
src/views/home/children/find/children/find1/index.vue

@@ -1,29 +0,0 @@
-<template>
-  <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
-    <van-swipe-item>1</van-swipe-item>
-    <van-swipe-item>2</van-swipe-item>
-    <van-swipe-item>3</van-swipe-item>
-    <van-swipe-item>4</van-swipe-item>
-  </van-swipe>
-  <button type="button" @click="increment">count is: {{ count }}</button>
-</template>
-
-<script lang="ts" setup>
-import { ref, watch } from "vue";
-let count = ref(1);
-watch(count, (val: Number) => {
-  console.log(val);
-});
-const increment = () => {
-  count.value++;
-};
-</script>
-
-<style scoped lang="scss">
-.my-swipe {
-  background-color: red;
-  width: 345px;
-  height: 100px;
-  margin: auto;
-}
-</style>

+ 0 - 29
src/views/home/children/find/index.vue

@@ -1,29 +0,0 @@
-<template>
-  <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
-    <van-swipe-item>1</van-swipe-item>
-    <van-swipe-item>2</van-swipe-item>
-    <van-swipe-item>3</van-swipe-item>
-    <van-swipe-item>4</van-swipe-item>
-  </van-swipe>
-  <button type="button" @click="increment">count is: {{ count }}</button>
-</template>
-
-<script lang="ts" setup>
-import { ref, watch } from "vue";
-let count = ref(1);
-watch(count, (val: Number) => {
-  console.log(val);
-});
-const increment = () => {
-  count.value++;
-};
-</script>
-
-<style scoped lang="scss">
-.my-swipe {
-  background-color: red;
-  width: 345px;
-  height: 100px;
-  margin: auto;
-}
-</style>

+ 82 - 58
src/views/home/children/test/components/sujectOne.vue

@@ -38,70 +38,94 @@
   </div>
 </template>
 
-<script lang="ts">
-import { defineComponent } from "vue";
+<script lang="ts" setup>
 import { useRouter } from "vue-router";
-
-export default defineComponent({
-  setup() {
-    const router = useRouter();
-    const gotoRoute = (obj: any) => {
-      router.push(obj);
-    };
-    const testList = [
+import { defineProps } from "vue";
+const props = defineProps(["query"]);
+const router = useRouter();
+const gotoRoute = (obj: any) => {
+  router.push(obj);
+};
+const testList = [
+  {
+    left: [
+      {
+        icon: "顺序练习",
+        name: "顺序练习",
+        route: { name: "exercise", query: { ...props.query } },
+      },
+      {
+        icon: "分类练习",
+        name: "分类练习",
+        route: {
+          name: "classify",
+          query: { ...props.query, path: "selectFlQustionInfo" },
+        },
+      },
+    ],
+    center: [
+      {
+        icon: "精选考题",
+        name: "精选考题500题",
+        route: {
+          name: "classify",
+          query: { ...props.query, path: "selectJxQustionInfo" },
+        },
+      },
+    ],
+    right: [
+      {
+        icon: "地方专题",
+        name: "地方专题",
+        route: {
+          name: "classify",
+          query: { ...props.query, path: "selectDfQustionInfo" },
+        },
+      },
+      {
+        icon: "错题收藏",
+        name: "错题收藏",
+        route: { name: "collection", query: { ...props.query } },
+      },
+    ],
+  },
+  {
+    left: [
+      {
+        icon: "真实考场模拟",
+        name: "考场模拟",
+        route: { name: "mockTest", query: { ...props.query } },
+      },
+      {
+        icon: "模拟成绩",
+        name: "模拟成绩",
+        route: { name: "testScores", query: { ...props.query } },
+      },
+    ],
+    center: [
+      {
+        icon: "模拟考试仿真题目",
+        name: "模拟考试仿真题目",
+        route: {
+          name: "mockTest",
+          query: { ...props.query },
+        },
+      },
+    ],
+    right: [
       {
-        left: [
-          { icon: "顺序练习", name: "顺序练习", route: { name: "exercise" } },
-          { icon: "分类练习", name: "分类练习", route: { name: "classify" } },
-        ],
-        center: [
-          {
-            icon: "精选考题",
-            name: "精选考题500题",
-            route: { name: "exercise" },
-          },
-        ],
-        right: [
-          { icon: "地方专题", name: "地方专题", route: { name: "topicType" } },
-          { icon: "错题收藏", name: "错题收藏", route: { name: "collection" } },
-        ],
+        icon: "考前须知",
+        name: "考前须知",
+        route: { name: "marked", query: { markdown: "考前须知" } },
       },
       {
-        left: [
-          {
-            icon: "真实考场模拟",
-            name: "考场模拟",
-            route: { name: "mockTest" },
-          },
-          { icon: "模拟成绩", name: "模拟成绩", route: { name: "testScores" } },
-        ],
-        center: [
-          {
-            icon: "模拟考试仿真题目",
-            name: "模拟考试仿真题目",
-            route: { name: "exercise" },
-          },
-        ],
-        right: [
-          {
-            icon: "考前须知",
-            name: "考前须知",
-            route: { name: "marked", query: { markdown: "考前须知" } },
-          },
-          {
-            icon: "学车必看",
-            name: "学车必看",
-            route: { name: "marked", query: { markdown: "学车必看" } },
-          },
-        ],
+        icon: "学车必看",
+        name: "学车必看",
+        route: { name: "marked", query: { markdown: "学车必看" } },
       },
-    ];
-    return {
-      gotoRoute,
-      testList,
-    };
+    ],
   },
-});
+];
 </script>
 
 <style lang="scss" scoped>

+ 3 - 5
src/views/home/children/test/components/swiper.vue

@@ -1,14 +1,12 @@
 <template>
   <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
     <van-swipe-item v-for="item in 4" :key="item">
-      <img src="@/assets/img/mb.png" class="banner">
+      <img src="@/assets/img/mb.png" class="banner" />
     </van-swipe-item>
   </van-swipe>
 </template>
 
-<script>
-export default {};
-</script>
+<script lang="ts" setup></script>
 
 <style lang="scss" scoped>
 .my-swipe {
@@ -17,7 +15,7 @@ export default {};
   height: 100px;
   margin: auto;
   margin-top: 12px;
-  .banner{
+  .banner {
     width: 100%;
     height: 100%;
   }

+ 16 - 13
src/views/home/children/test/components/userData.vue

@@ -2,24 +2,24 @@
   <van-nav-bar fixed placeholder>
     <template #left>
       <div class="vip-box">
-        <van-image
-          round
-          class="user-avatar"
-          src="https://img.yzcdn.cn/vant/cat.jpeg"
-        />
-        <span>我有六把刀</span>
+        <m-user-avatar class="user-avatar" />
+        <m-user-name />
       </div>
     </template>
     <template #right>
       <div class="vip-box">
-        <m-icon type="huiyuan" />
-        <span>VIP会员</span>
+        <m-icon class="vip-icon" type="huiyuan" />
+        <span>{{ expireTime ? expireTime : "开通会员" }}</span>
       </div>
     </template>
   </van-nav-bar>
 </template>
 
-<script lang='ts' setup>
+<script lang="ts" setup>
+import { computed } from "vue";
+import { useStore } from "vuex";
+const store = useStore();
+const expireTime = computed(() => store.getters.getUserData.expireTime);
 </script>
 
 <style lang="scss" scoped>
@@ -27,10 +27,13 @@
   display: flex;
   align-items: center;
   font-size: 17px;
-  .user-avatar{
-      width: 24px;
-      height: 24px;
-      margin-right: 2px;
+  .user-avatar {
+    width: 24px;
+    height: 24px;
+    margin-right: 2px;
   }
 }
+.vip-icon {
+  margin-right: 5px;
+}
 </style>

+ 22 - 25
src/views/home/children/test/index.vue

@@ -2,13 +2,13 @@
   <!-- 用户信息展示 -->
   <userData />
   <!-- 轮播图 -->
-  <swiper />
+  <!-- <swiper /> -->
   <!-- 用户做题预选界面 -->
   <van-tabs class="car-type" line-width="0" animated>
     <van-tab v-for="(carTypeItem, index) in carTypeList" :key="index">
       <template #title>
         <div class="car-choose">
-          <m-icon :type="carTypeItem.icon" class="img"/>
+          <m-icon :type="carTypeItem.icon" class="img" />
           <span>{{ carTypeItem.cert }}</span>
           <span>{{ carTypeItem.name }}</span>
         </div>
@@ -19,7 +19,10 @@
           v-for="(sujectItem, index) in carTypeItem.sujectList"
           :key="index"
         >
-          <component :is="sujectOne"></component>
+          <component
+            :is="sujectOne"
+            :query="{ ...carTypeItem.query, ...sujectItem.query }"
+          ></component>
         </van-tab>
       </van-tabs>
     </van-tab>
@@ -30,53 +33,47 @@
 import sujectOne from "./components/sujectOne.vue";
 import swiper from "./components/swiper.vue";
 import userData from "./components/userData.vue";
-
-
 import { ref } from "vue";
 
 const carTypeList = ref([
   {
     name: "轿车",
     cert: "C1/C2/C3",
-    icon:'jiaoche',
+    icon: "jiaoche",
+    query: { liceCar: 1 },
     sujectList: [
-      { name: "科目一" },
-      // { name: "科目二" },
-      // { name: "科目三" },
-      { name: "科目四" },
+      { name: "科目一", query: { subject1: 1 } },
+      { name: "科目四", query: { subject4: 1 } },
     ],
   },
   {
     name: "客车",
     cert: "A1/A3/B1",
-    icon:'keche',
+    icon: "keche",
+    query: { liceBus: 1 },
     sujectList: [
-      { name: "科目一" },
-      // { name: "科目二" },
-      // { name: "科目三" },
-      { name: "科目四" },
+      { name: "科目一", query: { subject1: 1 } },
+      { name: "科目四", query: { subject4: 1 } },
     ],
   },
   {
     name: "货车",
     cert: "A2/B2",
-    icon:'huoche',
+    icon: "huoche",
+    query: { liceTruck: 1 },
     sujectList: [
-      { name: "科目一" },
-      // { name: "科目二" },
-      // { name: "科目三" },
-      { name: "科目四" },
+      { name: "科目一", query: { subject1: 1 } },
+      { name: "科目四", query: { subject4: 1 } },
     ],
   },
   {
     name: "摩托车",
     cert: "D/E/F",
-    icon:'motuoche',
+    icon: "motuoche",
+    query: { liceMoto: 1 },
     sujectList: [
-      { name: "科目一" },
-      // { name: "科目二" },
-      // { name: "科目三" },
-      { name: "科目四" },
+      { name: "科目一", query: { subject1: 1 } },
+      { name: "科目四", query: { subject4: 1 } },
     ],
   },
 ]);

+ 31 - 25
src/views/home/children/user/index.vue

@@ -1,44 +1,47 @@
 <template>
   <div class="user">
-    <van-cell
-      title="我有六把刀"
-      icon="shop-o"
-      label="未绑定手机号"
-      is-link
-      center
-    >
+    <van-cell label="未绑定手机号" is-link center>
       <template #icon>
-        <van-image
-          round
-          class="user-avatar"
-          src="https://img.yzcdn.cn/vant/cat.jpeg"
-        />
+        <m-user-avatar class="user-avatar" />
+      </template>
+      <template #title>
+        <m-user-name />
       </template>
     </van-cell>
     <van-cell-group class="group">
-      <van-cell title="会员有效期" value="内容" is-link center>
+      <van-cell title="我要提现" value="" is-link center>
         <template #icon>
           <m-icon type="hyyxq" class="cell-icon" />
         </template>
       </van-cell>
-      <van-cell title="激活码" is-link center>
-        <template #icon>
-          <m-icon type="jihuoma" class="cell-icon" />
-        </template>
-      </van-cell>
-      <van-cell title="专属老师" is-link center>
+      <van-cell
+        title="会员有效期"
+        :value="expireTime ? expireTime : '开通会员'"
+        is-link
+        center
+      >
         <template #icon>
-          <m-icon type="zsls" class="cell-icon" />
+          <m-icon type="hyyxq" class="cell-icon" />
         </template>
       </van-cell>
     </van-cell-group>
     <van-cell-group class="group">
-      <van-cell title="版本更新" is-link center>
+      <van-cell
+        title="0.01捐款(内部专用)"
+        is-link
+        center
+        @click="loopPrepareOrder"
+      >
         <template #icon>
-          <m-icon type="bbgx" class="cell-icon" />
+          <m-icon type="fkbz" class="cell-icon" />
         </template>
       </van-cell>
-      <van-cell title="反馈帮助" is-link center url="https://support.qq.com/product/359609">
+      <van-cell
+        title="反馈帮助"
+        is-link
+        center
+        url="https://support.qq.com/product/359609"
+      >
         <template #icon>
           <m-icon type="fkbz" class="cell-icon" />
         </template>
@@ -48,9 +51,12 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, defineComponent, computed, watch } from "vue";
+import * as API from "@/api";
+import { computed } from "vue";
+const loopPrepareOrder = API.LoopPrepareOrder;
 import { useStore } from "vuex";
-import { useRouter, useRoute } from "vue-router";
+const store = useStore();
+const expireTime = computed(() => store.getters.getUserData.expireTime);
 </script>
 
 <style scoped lang="scss">

+ 21 - 3
src/views/login/index.vue

@@ -15,14 +15,32 @@
   </div>
 </template>
 
+<script lang="ts">
+import { defineComponent } from "vue";
+export default defineComponent({
+  name: "login",
+});
+</script>
+
 <script lang="ts" setup>
-import { useRouter } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
 import * as API from "@/api";
 import { ref } from "vue";
+import { useStore } from "vuex";
 const router = useRouter();
+const route = useRoute();
+const store = useStore();
 const loginState = ref(0);
-API.login("1231231").then((res) => {
-  if (res.data.code == 200) {
+
+API.login(route.query.code).then(async (res) => {
+  if (res.code == 200) {
+    store.commit("setToken", res.data.token);
+    store.commit("setUserData", res.data.wxUserInfo);
+    let userDataRes = await API.userInfo();
+    store.commit("setUserData", {
+      ...store.getters.getUserData,
+      expireTime: userDataRes.data.data.expireTime,
+    });
     loginState.value = 1;
     router.push("/home/test");
   } else {

+ 11 - 0
src/views/mockTest/components/hooks.ts

@@ -0,0 +1,11 @@
+import { defineEmits } from "vue";
+
+export const useNext = () => {
+  const emits = defineEmits(["next"]);
+  const gotoNextPage = () => {
+    emits("next");
+  };
+  return {
+    gotoNextPage,
+  };
+};

+ 5 - 4
src/views/mockTest/components/initMockTest.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="header-back">
-    <m-nav-bar title="模拟考试" />
+    <m-nav-bar :transparent="true" title="模拟考试" />
     <div class="user-data">
       <m-user-avatar />
       <m-user-name />
@@ -32,16 +32,17 @@
       width="266px"
       height="40px"
       text="开始考试"
-      @click="gotoTest"
+      @click="gotoNextPage"
     />
   </div>
 </template>
 
+<script lang="ts"></script>
+
 <script lang="ts" setup>
 import { defineEmits } from "vue";
-
 const emits = defineEmits(["next"]);
-const gotoTest = () => {
+const gotoNextPage = () => {
   emits("next");
 };
 </script>

+ 14 - 13
src/views/mockTest/components/startTest.vue

@@ -28,10 +28,7 @@
     <div class="problem">
       <span class="type">{{ topicType(currentAnswer.type) }}</span>
       <span class="text">{{ currentAnswer.explain }}</span>
-      <img
-        src="https://t1-1305573081.file.myqcloud.com/image/5.jpg"
-        class="img"
-      />
+      <img v-if="currentAnswer.image" :src="currentAnswer.image" class="img"/>
     </div>
     <!-- 选择内容 -->
     <div v-if="true">
@@ -131,10 +128,10 @@
       <m-icon type="shoucanghui" size="25px" />
       <span>收藏</span>
     </div>
-    <div class="function-item" @click="answerAudioPlay">
+    <!-- <div class="function-item" @click="answerAudioPlay">
       <m-icon type="a-dtda" size="25px" />
       <span>读题+答案</span>
-    </div>
+    </div> -->
     <div class="function-item" @click="issueAudioPlay">
       <m-icon type="duti" size="25px" />
       <span>读题</span>
@@ -240,7 +237,7 @@
 
 <script lang="ts" setup>
 import * as Api from "@/api";
-import { useRouter } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
 import {
   ref,
   watch,
@@ -252,6 +249,7 @@ import {
 } from "vue";
 import { Dialog } from "vant";
 const router = useRouter();
+const route = useRoute();
 const onClickLeft = () => {
   router.back();
 };
@@ -316,12 +314,15 @@ console.log(topicList);
 
 //API请求接口数据
 onBeforeMount(async () => {
-  const topicListApiRes = Api.getTopicList();
-  setTimeout(() => {
-    topicList.value = topicListApiRes.data.list;
-    idIndex.value = 200;
-    total.value = 1999;
-  }, 1000);
+  const topicListApiRes = await Api.getTopicList({
+    ...route.query,
+    pageNum: 1,
+    pageSize: 100,
+    isRand: true,
+  });
+  topicList.value = topicListApiRes.list;
+  idIndex.value = currentAnswerIndex.value;
+  total.value = 100;
 });
 
 //当前题目数据

+ 77 - 12
src/views/testScores/index.vue

@@ -1,12 +1,15 @@
 <template>
   <div class="header-back">
-    <m-nav-bar title="模拟考试" />
+    <m-nav-bar :transparent="true" title="模拟考试" style="color: #ffffff" />
     <div class="user-data">
       <div class="left">
         <m-user-avatar />
         <div class="name">
           <m-user-name />
-          <span>最高成绩<span class="grade">60</span>分</span>
+          <span
+            >最高成绩<span class="grade">{{ testScoresInfo.maxScore }}</span
+            >分</span
+          >
         </div>
       </div>
       <m-button class="continue" width="90px" height="30px" text="继续考试" />
@@ -14,15 +17,24 @@
   </div>
   <div class="summary content-box">
     <div class="item">
-      <div><span class="number">10</span>次</div>
+      <div>
+        <span class="number">{{ testScoresList.length }}</span
+        >次
+      </div>
       <div>考试次数</div>
     </div>
     <div class="item">
-      <div><span class="number">20</span>次</div>
+      <div>
+        <span class="number">{{ testScoresInfo.avgScore }}</span
+        >分
+      </div>
       <div>平均成绩</div>
     </div>
     <div class="item">
-      <div><span class="number">0</span>次</div>
+      <div>
+        <span class="number">{{ testScoresInfo.forecastScore }}</span
+        >分
+      </div>
       <div>成绩预测</div>
     </div>
   </div>
@@ -34,18 +46,71 @@
         <th>分数</th>
         <th>时间</th>
       </tr>
-      <tr v-for="(item,index) in 10" :key="index">
-        <td>摩托车</td>
-        <td>科目一</td>
-        <td>0</td>
-        <td>2021-05-20 15:20:30</td>
+      <tr v-for="(item, index) in testScoresList" :key="index">
+        <td>{{ item.type }}</td>
+        <td>{{ item.kskm }}</td>
+        <td>{{ item.score }}</td>
+        <td>{{ item.createTime }}</td>
       </tr>
     </table>
   </div>
 </template>
 
-<script>
-export default {};
+<script lang="ts">
+import { getTestScoresList, getTestScoresInfo } from "@/api";
+import { ref, onBeforeMount } from "vue";
+/**
+ * 考试成绩数据结构
+ */
+interface TestScores {
+  createTime: string; //考试时间
+  kskm: string; //科目
+  score: number; //分数
+  type: string; //车型
+}
+/**
+ * 模拟考成绩列表
+ */
+const useTestScoresList = () => {
+  const testScoresList = ref<TestScores[]>([]);
+  onBeforeMount(async () => {
+    let res = await getTestScoresList();
+    testScoresList.value = res.rows;
+  });
+  return {
+    testScoresList,
+  };
+};
+/**
+ * 成绩信息数据结构
+ */
+interface TestScoresInfo {
+  avgScore: number; //平均成绩
+  forecastScore: number; //预测成绩
+  maxScore: number; //最大成绩
+}
+/**
+ * 最大成绩,平均成绩,预测成绩
+ */
+const useTestScoresInfo = () => {
+  const testScoresInfo = ref<TestScoresInfo>({
+    avgScore: 0,
+    forecastScore: 0,
+    maxScore: 0,
+  });
+  onBeforeMount(async () => {
+    let res = await getTestScoresInfo();
+    testScoresInfo.value = res.data;
+  });
+  return {
+    testScoresInfo,
+  };
+};
+</script>
+
+<script lang="ts" setup>
+const { testScoresList } = useTestScoresList();
+const { testScoresInfo } = useTestScoresInfo();
 </script>
 
 <style scoped lang="scss">

+ 7 - 0
vite.config.ts

@@ -12,5 +12,12 @@ export default defineConfig({
   },
   server: {
     host: "0.0.0.0",
+    proxy: {
+      "/api": {
+        target: "http://192.168.8.213:8080",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/api/, ""),
+      },
+    },
   },
 });