Selaa lähdekoodia

恢复为新版本

zhangyujun 3 vuotta sitten
vanhempi
commit
655f773c7d
56 muutettua tiedostoa jossa 3991 lisäystä ja 0 poistoa
  1. 8 0
      .prettierrc.js
  2. 27 0
      cert/6353984_jpcj-h5.zzxcx.net.key
  3. 61 0
      cert/6353984_jpcj-h5.zzxcx.net.pem
  4. 13 0
      index.html
  5. 24 0
      postcss.config.js
  6. 18 0
      src/App.tsx
  7. 94 0
      src/api/api.ts
  8. 22 0
      src/api/request.ts
  9. 28 0
      src/api/types/questionTwoList.d.ts
  10. 51 0
      src/api/types/selectTestQuestionInfo.d.ts
  11. 26 0
      src/api/types/studentUserInfo.d.ts
  12. BIN
      src/assets/img/aprilExam.png
  13. BIN
      src/assets/img/backButton.png
  14. BIN
      src/assets/img/beforeTopics.png
  15. BIN
      src/assets/img/blueBus.png
  16. BIN
      src/assets/img/blueCar.png
  17. BIN
      src/assets/img/blueMoto.png
  18. BIN
      src/assets/img/blueTruck.png
  19. BIN
      src/assets/img/nextTopics.png
  20. BIN
      src/assets/img/noimg.png
  21. BIN
      src/assets/img/okimg.png
  22. BIN
      src/assets/img/subject1@2x.png
  23. BIN
      src/assets/img/subject4@2x.png
  24. BIN
      src/assets/img/whiteBus.png
  25. BIN
      src/assets/img/whiteCar.png
  26. BIN
      src/assets/img/whiteMoto.png
  27. BIN
      src/assets/img/whiteTruck.png
  28. BIN
      src/assets/img/开车的司机2-02@2x.png
  29. 47 0
      src/assets/json/questionTwoList.json
  30. 21 0
      src/components/HelloWord.tsx
  31. 157 0
      src/components/mask/beforeSubmitMask.vue
  32. 175 0
      src/components/mask/submitMask.vue
  33. 32 0
      src/hooks/countdown.ts
  34. 532 0
      src/hooks/examTest.ts
  35. 0 0
      src/hooks/phone.ts
  36. 17 0
      src/hooks/stringMap.ts
  37. 20 0
      src/main.ts
  38. 47 0
      src/router/index.ts
  39. 4 0
      src/shim.d.ts
  40. 4 0
      src/source.d.ts
  41. 20 0
      src/store/index.ts
  42. 11 0
      src/store/state.ts
  43. 1 0
      src/style/index.scss
  44. 22 0
      src/style/main.scss
  45. 13 0
      src/style/reset.scss
  46. 70 0
      src/utils/phone.ts
  47. 60 0
      src/utils/scoreCirle.ts
  48. 16 0
      src/views/About.tsx
  49. 18 0
      src/views/Home.tsx
  50. 25 0
      src/views/Like.vue
  51. 742 0
      src/views/aprilExam/test.vue
  52. 235 0
      src/views/exam/begin.vue
  53. 98 0
      src/views/exam/score.vue
  54. 1160 0
      src/views/exam/test.vue
  55. 39 0
      tsconfig.json
  56. 33 0
      vite.config.ts

+ 8 - 0
.prettierrc.js

@@ -0,0 +1,8 @@
+module.exports = {
+  semi: true,
+  trailingComma: "all",
+  singleQuote: true,
+  printWidth: 100,
+  tabWidth: 2,
+  endOfLine:"auto"
+};

+ 27 - 0
cert/6353984_jpcj-h5.zzxcx.net.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAwQN22+JQZKz74mBHdqsJOJuOP3ClSudYhEpw+rWKNHGZg4lz
+ZAMQCYqQQyd5uXVMGup6Pr0wKWLAdT835ADOKzInTme2X9NqRRWvprn1pBPnNuJ3
+vz233oHpe1At2rjHjj+yU9X+RqGxY37DDBJ6O2wNxUsF3av1fowVmboh1EkmwQH2
+gbMBPgIAF7Z/ka0Bv52K3LLV9DRit1lNJLxzFP2QLOomMKGP1YztVImx8M5eLTbI
+uoM3GlJruxNECOe+f4jQQF6QiTwjCFLJbWrj4S76wWfDA350J3WmpqsFwZ1g38p+
+cuiN/0JLMjmFyGuQeIxRE5/BnmCdhumIO38P2wIDAQABAoIBAB3HKgxMgzi1E3te
+fUgKpTaNkUmqWGYRATAPyBR186eZqySHDT1t14iRTyZ9LaN2PFKewFNznr2T1buB
+EF9l3Gj/qN7I03M/S9LywJHJeONiiraGBBGGNRroEhyLRzfKcEc6/ok+p6dABP0j
+rnXrV95ZElrQ80u63voyJJb+B4AXsIDCObl9ZsZ2rcOSRFa+4XeUlFJVi/Crl3DP
+1bBq5Evvz9MhufdrjHojMG8kqG19JtkqnsRImcvGvAef4H54TKZInUkZOad6AstN
+hPGyB1pqgNAZRUcifpXUhRRkxIrFFLFJPsGW8ao80LneDQuNIaH6M9+ky/rJZnM2
+BgNiNqECgYEA4eBQvGE6TqohIr5gWNGZ8t7hoQvMcgw5GO5+SoyCTSv6o99WzVmW
+/c3l3ALKWOIwaDq8wcR9nizAblNcGI1q7dWU6cptYtf8TG6KlxisymGLLg/pE0Pk
+L59Lpj+LZaHSvjDS6HJL4LE3/nMsAEZaIYXIfGG7py2LkI3QFDqDsYsCgYEA2sEt
+VA+Hp4RFOSYxAr8U2bJFxw4mXURh4LncFg770e56QeHuQ9JRpLKqUb9mywOWr4bv
+aFcV4B2JSPkf6VCtrXsoF/D0ORftHUiGic12zNjKohD4dg/EqKKEuMCax03oK8a+
+Q0OjjqoP/1qxsR4NwepwbxgCv67NrUb381KURPECgYAX60Uhbbpa8jEoGLjjLu76
+hRenkjRIL3ssZLUC9/Ykbe/QwqnSfRY8Mj2RGsqKtwkhQHcUndYfleWayUELaI5W
+OTPXvhPvT6fsABi0/2vs0n+GlTC9VYcoc9ny9O+yB3zlnw32h4P5e0rOqyuRq+GH
+gz9DzKWugmgxGpNaUQf5GQKBgGgbw8zbzz3ET4WRTYX3Fp88nsTLUSrWHpbrHXlK
+DEKu8nRkaBbTA5QUi8Z4Bx3bSV2kJFBOEdUleW7HUY6inOWRVVV/kVuSrYBgQddX
+d5CqJFXZKcZWXsu6/NtInuD3jOU4otg0WqTM/uwSZ7rZiMafIn9cuAIt2Sj8O4kL
+5hChAoGBAJQ/VM1kcMIhfpTSJhf40QaPA77caXOTaxKDY9RJh6VW48u/R3E3JgRQ
+FWuqa05+RnPQPSBPOCsD9uPoQDKzJ0BvH26RAJ4Fiqqbu26dFHRVqVA1bD87FL2G
+QnYZkQYOLMIbGJpIPh4hgDOZlLJdPCZJ4/Lqc4wRl6t+/gS1W1RS
+-----END RSA PRIVATE KEY-----

+ 61 - 0
cert/6353984_jpcj-h5.zzxcx.net.pem

@@ -0,0 +1,61 @@
+-----BEGIN CERTIFICATE-----
+MIIF9jCCBN6gAwIBAgIQDnko4eDQ3FiCq4TD5KitVzANBgkqhkiG9w0BAQsFADBu
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg
+RFYgVExTIENBIC0gRzEwHhcNMjEwOTI3MDAwMDAwWhcNMjIwOTI3MjM1OTU5WjAc
+MRowGAYDVQQDExFqcGNqLWg1Lnp6eGN4Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMEDdtviUGSs++JgR3arCTibjj9wpUrnWIRKcPq1ijRxmYOJ
+c2QDEAmKkEMnebl1TBrqej69MCliwHU/N+QAzisyJ05ntl/TakUVr6a59aQT5zbi
+d789t96B6XtQLdq4x44/slPV/kahsWN+wwwSejtsDcVLBd2r9X6MFZm6IdRJJsEB
+9oGzAT4CABe2f5GtAb+dityy1fQ0YrdZTSS8cxT9kCzqJjChj9WM7VSJsfDOXi02
+yLqDNxpSa7sTRAjnvn+I0EBekIk8IwhSyW1q4+Eu+sFnwwN+dCd1pqarBcGdYN/K
+fnLojf9CSzI5hchrkHiMUROfwZ5gnYbpiDt/D9sCAwEAAaOCAuAwggLcMB8GA1Ud
+IwQYMBaAFFV0T7JyT/VgulDR1+ZRXJoBhxrXMB0GA1UdDgQWBBQ1RnSvbnnBGp7l
+4ukPv4R5KbtAIDAcBgNVHREEFTATghFqcGNqLWg1Lnp6eGN4Lm5ldDAOBgNVHQ8B
+Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD4GA1UdIAQ3
+MDUwMwYGZ4EMAQIBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQu
+Y29tL0NQUzCBgAYIKwYBBQUHAQEEdDByMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
+cC5kaWdpY2VydC5jb20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRzLmRpZ2lj
+ZXJ0LmNvbS9FbmNyeXB0aW9uRXZlcnl3aGVyZURWVExTQ0EtRzEuY3J0MAkGA1Ud
+EwQCMAAwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB2ACl5vvCeOTkh8FZzn2Ol
+d+W+V32cYAr4+U1dJlwlXceEAAABfCaJUJwAAAQDAEcwRQIhAInRm9HVbRmpqVS9
+hAForE4jV6FkncVt/7dOh8EDs0dxAiAlj/LzSu0BB9mpo59PzRYb4YqJ1QMbq9Ei
+AhRkZL5kLgB2AFGjsPX9AXmcVm24N3iPDKR6zBsny/eeiEKaDf7UiwXlAAABfCaJ
+UHkAAAQDAEcwRQIhALjTxVpf6aLydhshlgXUE40fU0bJ2NOiQj7RhyH1bowiAiBr
+CWC5Kbv8NGSF0y44tuqTaSsrY/K8r63/XgRlMC+t8wB1AEHIyrHfIkZKEMahOglC
+h15OMYsbA+vrS8do8JBilgb2AAABfCaJUEAAAAQDAEYwRAIgGw3wFwpeC5UVC4OV
+jUP6yYi1XMmlWiqx8MnKfA1bLDUCIG2jgXAGdLtZ+a+agkULO4Q+/TVGHzUq3ew6
+bNnVaLl3MA0GCSqGSIb3DQEBCwUAA4IBAQCyEv6Q2N2SjEs8fKiM5FEeXIwG1an8
+UcS/r0+M7q839kHtiA2iZig0ia7Sa7eY3OQX6dGhdfX9QhSob7WxbTNAbjTJCKdt
+SO3Oh1xF04+NeaBTS3aGiKSUduUh2WtVR3sHWeIttd9BqR89OkkEUbFS9/Xaa3II
+7xVsVrtkXEW4fFzmDkYifJNhNG0VOGNf6Gi1RB1coralyXTsGc9ivxhrTB7PSixw
+O2Sp8X3XBwlMt3fRKa5zHTpcBcU7/RuwyCvGOTXyUiWO8AbXnLOEG1wOhSM/+ONB
+FkbOyKyYJdvlOJGMlNwP5JMaTLlvkf0SxkGUVTR+Pbd8DwZUAHUXyzBE
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH
+MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc
+oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo
+lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj
+pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h
+yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n
+wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M
+pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf
+BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw
+HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
+Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu
+Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
+/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
+MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B
+SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW
+M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV
+4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ
+sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy
+rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg==
+-----END CERTIFICATE-----

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <link rel="icon" href="/favicon.ico" />
+  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" />
+  <title>真实模拟考试</title>
+</head>
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.ts"></script>
+</body>
+</html>

+ 24 - 0
postcss.config.js

@@ -0,0 +1,24 @@
+//postcss.config.js文件
+module.exports = {
+    plugins: {
+      'postcss-px-to-viewport': {
+        unitToConvert: 'px', //需要转换的单位,默认为"px"
+        viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度
+        viewportHeight: 667,//视窗的高度,根据375设备的宽度来指定,一般指定667,也可以不配置
+        unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
+        propList: ['*'], // 能转化为vw的属性列表
+        viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
+        fontViewportUnit: 'vw', //字体使用的视口单位
+        selectorBlackList: ['.ignore-', '.hairlines'], //指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
+        minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
+        mediaQuery: false, // 允许在媒体查询中转换`px`
+        replace: true, //是否直接更换属性值,而不添加备用属性
+        exclude: [
+          /RightBar/,
+          /gotop.vue/,
+        ], //忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
+        landscape: false, //是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
+        landscapeUnit: 'vw', //横屏时使用的单位
+      }
+    }
+  }

+ 18 - 0
src/App.tsx

@@ -0,0 +1,18 @@
+import { defineComponent } from 'vue';
+import {RouterLink, RouterView} from 'vue-router';
+import './style/main.scss'
+
+export default defineComponent({
+  name: 'App',
+  setup() {
+    return () => (
+      <>
+        {/* <div id="nav">
+          <RouterLink to="/">Home</RouterLink> |
+          <RouterLink to="/about">About</RouterLink>
+        </div> */}
+        <RouterView/>
+      </>
+    );
+  }
+});

+ 94 - 0
src/api/api.ts

@@ -0,0 +1,94 @@
+import { AxiosPromise, AxiosResponse } from "axios";
+import request from "./request";
+//用户授权码模式登录
+function loginCode(data?: {
+    authorizationCode: string
+
+}) {
+    return request({
+        url: "login/code",
+        method: "post",
+        data: data
+
+    })
+
+}
+//用户授权码模式登录--测试
+function loginCodeTest() {
+    return request({
+        url: "login/code/test",
+        method: "post",
+    })
+
+}
+//查询模拟考试题库
+function studentQustionInfoSelectTestQuestionInfo(params: {
+    liceBus: string,
+    liceCar: string,
+    liceMoto: string,
+    liceTruck: string,
+    subject: number,
+}): AxiosPromise<selectTestQuestionInfo.response> {
+    return request({
+        url: "student/qustion/info/selectTestQuestionInfo",
+        method: "get",
+        params
+
+    })
+
+}
+//查询模拟考试题库(通用)
+function openApiqustionInfoSelectTestQuestionInfo(params: {
+    liceBus: string,
+    liceCar: string,
+    liceMoto: string,
+    liceTruck: string,
+    subject: number,
+}): AxiosPromise<selectTestQuestionInfo.response> {
+    return request({
+        url: "open-api/qustion/info/selectTestQuestionInfo",
+        method: "get",
+        params
+
+    })
+
+}
+//查询用户信息
+function studentUserInfo(): AxiosPromise<studentUserInfo.response> {
+    return request({
+        url: "student/qustion/info/selectTestQuestionInfo",
+        method: "get",
+    })
+
+}
+//查询202204题库列表
+async function questionTwoList(params: {
+    pageNum: number,
+    pageSize: number,
+    cartype: "0" | "1" | "2" | "3",
+    kemu: number,
+    explainJs?: string
+}): Promise<AxiosPromise<questionTwoList.response>> {
+    let res: AxiosResponse<questionTwoList.response> = await request({
+        url: "https://admin.zzxcx.net/zzjs-admin/open-api/question/two/list",
+        method: "get",
+        params
+    })
+    //
+    return res
+}
+export default {
+    loginCode,
+    loginCodeTest,
+    studentQustionInfoSelectTestQuestionInfo,
+    studentUserInfo,
+    openApiqustionInfoSelectTestQuestionInfo,
+    questionTwoList
+
+
+}
+
+function AxiosPromise<T>() {
+    throw new Error("Function not implemented.");
+}
+

+ 22 - 0
src/api/request.ts

@@ -0,0 +1,22 @@
+import axios from "axios";
+import { requestLogger, responseLogger } from "axios-logger";
+const request = axios.create({
+	// baseURL: import.meta.env.MODE === "production" ? "/prod-api" : "/dev-api",
+});
+request.interceptors.request.use(config=>{
+	// let token = window.localStorage.getItem("token")
+	if (config.headers.isToken !== false) {
+	//	config.headers["Authorization"] = "Bearer " +token; // 让每个请求携带自定义token 请根据实际情况自行修改
+		config.headers["Authorization"]=""
+	}
+	return config;
+
+})
+request.interceptors.response.use((res) => {
+	// console.log(res)
+	return res;
+});
+
+export default request;
+
+

+ 28 - 0
src/api/types/questionTwoList.d.ts

@@ -0,0 +1,28 @@
+declare namespace questionTwoList {
+    interface row {
+        "an1": string,
+        "an2": string,
+        "an3": string,
+        "an4": string,
+        "an5": string,
+        "an6": string,
+        "an7": string,
+        "cartype": string,
+        "explainJs": string,
+        "id": number,
+        "image": string,
+        "kemu": number,
+        "name": string,
+        "num": number,
+        "rightAnswer": number,
+        "type": number,
+        "isExplainJs"?:Boolean,
+        "userAnswer"?:number
+    }
+    interface response {
+        code: number,
+        msg: string,
+        rows: row[]
+    }
+
+}

+ 51 - 0
src/api/types/selectTestQuestionInfo.d.ts

@@ -0,0 +1,51 @@
+declare namespace selectTestQuestionInfo {
+    interface response {
+        total:number,
+        msg:string,
+        code:number,
+        rows:[row]
+    }
+    interface row {
+        userAnswer: string;
+        createTime: string,
+        titlekeyword?: string,
+        id: number,
+        answer: string,
+        answerkeyword: null | string,
+        explainGif: string,
+        explainJq: string,
+        explainJs: string,
+        explainMp3: string,
+        image: string|null,
+        imageYdt: string|null,
+        issue: string,
+        opts: string,
+        optsArr?:string[],
+        skillkeyword: string,
+        issuemp3: string,
+        answermp3: string,
+        explainjsmp3: string,
+        liceCar: string,
+        liceBus:string,
+        liceTruck:string,
+        liceMoto: string,
+        sequeIssue: string,
+        classIssue: string,
+        placeIssue: string,
+        excellIssue: string,
+        copyIssue: string,
+        mockIssue: string,
+        sequeIssueName: string,
+        placeIssueName: null,
+        excellIssueName: string,
+        classIssueName: string,
+        questionType: number,
+        subject: number,
+        classSort: number,
+        excellSort: number,
+        sequeSort: number,
+        placeSort: null
+
+    }
+
+}

+ 26 - 0
src/api/types/studentUserInfo.d.ts

@@ -0,0 +1,26 @@
+declare namespace studentUserInfo{
+    interface data {
+        achievement:number,
+        achievementSettled:number,
+        areaName:string,
+        cityName:string,
+        expireTime:string,
+        headImage:string,
+        id:Number,
+        nickName:string,
+        openid:string,
+        phone:number,
+        profitPrice:number,
+        saleType:number,
+        schoolName:string,
+        status:0|1,
+        unionId:string
+
+    }
+    interface response {
+        code:number,
+        msg:string,
+        data:data
+    }
+
+}

BIN
src/assets/img/aprilExam.png


BIN
src/assets/img/backButton.png


BIN
src/assets/img/beforeTopics.png


BIN
src/assets/img/blueBus.png


BIN
src/assets/img/blueCar.png


BIN
src/assets/img/blueMoto.png


BIN
src/assets/img/blueTruck.png


BIN
src/assets/img/nextTopics.png


BIN
src/assets/img/noimg.png


BIN
src/assets/img/okimg.png


BIN
src/assets/img/subject1@2x.png


BIN
src/assets/img/subject4@2x.png


BIN
src/assets/img/whiteBus.png


BIN
src/assets/img/whiteCar.png


BIN
src/assets/img/whiteMoto.png


BIN
src/assets/img/whiteTruck.png


BIN
src/assets/img/开车的司机2-02@2x.png


+ 47 - 0
src/assets/json/questionTwoList.json

@@ -0,0 +1,47 @@
+{
+	"code": 200,
+	"msg": "成功",
+	"rows": [
+		{
+			"createTime": "2022-03-30 10:49:00",
+			"updateTime": "2022-03-30 10:54:21",
+			"an1": "正确",
+			"an2": "错误",
+			"an3": "",
+			"an4": "",
+			"an5": "",
+			"an6": "",
+			"an7": "",
+			"cartype": "",
+			"explainJs": "《机动车驾驶证申请和使用规定》第八十条:机动车驾驶人在实习期内发生的道路交通安全违法行为被记满12分的,注销其实习的准驾车型驾驶资格",
+			"id": 0,
+			"image": "",
+			"kemu": 0,
+			"name": "",
+			"num": 0,
+			"rightAnswer": 1,
+			"type": 0
+		},
+		{
+			"createTime": "2022-03-30 10:49:00",
+			"updateTime": "2022-03-30 10:54:21",
+			"id": 93,
+			"num": 93,
+			"name": "允许自学直考人员使用图中小型客车教练车,在学车专用标识签注的指导人员随车指导下学习驾驶。",
+			"image": "https://wos.58cdn.com.cn/XyVuTsRqXyf/question/22670-1561968327494.webp",
+			"rightAnswer": 2,
+			"explainJs": "《机动车驾驶证申领和使用规定》第二十六条:实行小型汽车、小型自动挡汽车驾驶证自学直考的地方,申请人可以使用加装安全辅助装置的自备机动车,在具备安全驾驶经历等条件的人员随车指导下,按照公安机关交通管理部门指定的路线、时间学习驾驶技能,按照第二十三条的规定申请相应准驾车型的驾驶证。",
+			"type": 1,
+			"an1": "正确",
+			"an2": "错误",
+			"an3": null,
+			"an4": null,
+			"an5": null,
+			"an6": null,
+			"an7": null,
+			"kemu": 1,
+			"cartype": "0,1,2"
+		}
+	],
+	"total": 0
+}

+ 21 - 0
src/components/HelloWord.tsx

@@ -0,0 +1,21 @@
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'App',
+  props: {
+    msg: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(prop) {
+    const count = ref(0)
+    return () => (
+      <>
+        <h1>{prop.msg}</h1>
+        <button onClick={() => {count.value ++}}>count is: { count.value }</button>
+        <p>Edit <code>components/HelloWorld.vue</code> to test hot module replacement.</p>
+      </>
+    );
+  },
+});

+ 157 - 0
src/components/mask/beforeSubmitMask.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="mask" v-show="show">
+    <div>
+      <div class="dialog">
+        <div class="title">
+          <span>考试确认窗口</span>
+        </div>
+        <div class="" style="background: #306ace">
+          <div class="mid">
+            <div class="mid-line1">操作提示:</div>
+            <div class="mid-line2">
+              你当前考试答对{{ correctScore }}题,答错{{ errorScore }}题,未答{{
+                total - correctScore - errorScore
+              }}题
+            </div>
+            <div class="mid-line3">
+              <span>1.点击【确认交卷】,将提交考试成绩,结束考试!</span>
+            </div>
+            <div class="mid-line4">
+              <span>2.点击【继续考试】,将关闭本窗口,继续考试!</span>
+            </div>
+          </div>
+        </div>
+
+        <div class="bottom">
+          <button @click="sendPaper">确认交卷</button>
+          <button @click="cancelSubmit">继续考试</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineProps, reactive, toRefs } from 'vue';
+
+export default {
+  setup(props, { emit }) {
+    const state = reactive({
+      count: 0,
+    });
+    const sendPaper = () => {
+      emit('confirm');
+    };
+    const cancelSubmit = () => {
+      emit('cancel');
+    };
+
+    return {
+      ...toRefs(state),
+      props,
+      sendPaper,
+      cancelSubmit,
+    };
+  },
+  props: {
+    show: {
+      type: Boolean,
+      required: true,
+      default: false,
+    },
+    correctScore: {
+      type: Number,
+      required: true,
+      default: 0,
+    },
+    errorScore: {
+      type: Number,
+      required: true,
+      default: 0,
+    },
+    total: {
+      type: Number,
+      required: true,
+      default: 0,
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+button {
+  border-radius: 0;
+  border: none;
+}
+.mask {
+  position: fixed;
+  top: 0;
+  width: 100vw;
+  height: 100vh;
+  background-color: rgb(0, 0, 0, 0.65);
+  z-index: 10;
+  display: flex;
+  align-content: center;
+  align-items: center;
+  justify-content: center;
+  .dialog {
+    width: 381px;
+    height: 210px;
+    background: #fff;
+    .title {
+      width: 100%;
+      line-height: 40px;
+      height: 40px;
+      background: #306ace;
+      color: #fff;
+      font-size: 14px;
+      font-weight: 700;
+    }
+    .mid {
+      width: 350px;
+      margin: 0 auto;
+      background: #fff;
+      height: 115px;
+      font-size: 14px;
+      padding-left: 18px;
+      padding-top: 14px;
+      font-size: 13px;
+      // padding-right: 10px;
+      .mid-line1 {
+        line-height: 25px;
+        height: 25px;
+        text-align: left;
+        line-height: 25px;
+      }
+      .mid-line2 {
+        text-align: left;
+        line-height: 25px;
+      }
+      .mid-line3 {
+        text-align: left;
+        line-height: 25px;
+      }
+      .mid-line4 {
+        text-align: left;
+        line-height: 25px;
+      }
+    }
+    .bottom {
+      background: #306ace;
+      width: 100%;
+      height: 59px;
+      display: flex;
+      justify-content: center;
+      align-content: center;
+      align-items: center;
+      button:nth-child(1) {
+        height: 38px;
+        margin-right: 27px;
+      }
+      button:nth-child(2) {
+        height: 38px;
+      }
+    }
+  }
+}
+</style>

+ 175 - 0
src/components/mask/submitMask.vue

@@ -0,0 +1,175 @@
+<template>
+  <div class="mask">
+    <div>
+      <div class="dialog">
+        <div class="title">
+          <span>信息提示</span>
+        </div>
+        <div class="mid">
+          <div class="mid-line1">亲爱的考生:</div>
+          <div class="mid-line2">
+            您本次模拟考试得{{ correctScore }}分。<span v-if="correctScore >= 90">及格</span
+            ><span v-else>不及格</span>,祝你下次
+          </div>
+          <div class="mid-line3" v-if="correctScore >= 90">再接再励</div>
+          <div class="mid-line3" v-else>考试成功</div>
+        </div>
+        <div class="bottom">
+          <button
+            @click="
+              () => {
+                router.push({
+                  name: 'examBegin',
+                  query: {
+                    ...router.currentRoute.value.query,
+                  },
+                });
+              }
+            "
+          >
+            关闭
+          </button>
+          <div class="bottom-line">页面将在{{ time }}s内返回</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineProps, reactive, toRefs, ref } from 'vue';
+import router from '@/router/';
+import Phone from '@/utils/phone';
+export default {
+  setup(props, { emit }) {
+    const state = reactive({
+      count: 0,
+    });
+    const phone = new Phone();
+    const backView = phone.backView.bind(phone);
+    const time = ref(8);
+    const timer = window.setInterval((e) => {
+      time.value = time.value - 1;
+      if (time.value === 0) {
+        window.clearInterval(timer);
+        // backView()
+        router.push({
+          name: 'examBegin',
+          query: {
+            ...router.currentRoute.value.query,
+          },
+        });
+      }
+    }, 1000);
+    const sendPaper = () => {
+      emit('confirm');
+    };
+    const cancelSubmit = () => {
+      emit('cancel');
+    };
+
+    return {
+      ...toRefs(state),
+      props,
+      sendPaper,
+      cancelSubmit,
+      time,
+      timer,
+      router,
+      backView,
+    };
+  },
+  beforeUnmount() {
+    window.clearInterval(this.timer);
+  },
+  props: {
+    correctScore: {
+      type: Number,
+      required: true,
+      default: 0,
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+button {
+  border-radius: 0;
+  border: none;
+}
+.mask {
+  position: fixed;
+  top: 0;
+  width: 100vw;
+  height: 100vh;
+  background-color: rgb(0, 0, 0, 0.65);
+  z-index: 10;
+  display: flex;
+  align-content: center;
+  align-items: center;
+  justify-content: center;
+  .dialog {
+    width: 400px;
+    height: 205px;
+    background: #fff;
+    .title {
+      width: 100%;
+      line-height: 40px;
+      height: 40px;
+      background: #306ace;
+      color: #fff;
+      font-size: 12px;
+    }
+    .mid {
+      width: 100%;
+      background: #fff;
+      height: 100px;
+      padding-left: 10px;
+      padding-right: 10px;
+      font-size: 14px;
+      .mid-line1 {
+        line-height: 25px;
+        height: 25px;
+        text-align: left;
+      }
+      .mid-line2 {
+        text-align: left;
+        line-height: 30px;
+      }
+      .mid-line3 {
+        text-align: left;
+        line-height: 30px;
+      }
+      .mid-line4 {
+        text-align: left;
+        line-height: 30px;
+      }
+    }
+    .bottom {
+      background: #306ace;
+      width: 100%;
+      height: 70px;
+      display: flex;
+      justify-content: center;
+      flex-wrap: wrap;
+      align-items: flex-start;
+      align-content: flex-start;
+
+      button:nth-child(1) {
+        height: 30px;
+        margin-top: 10px;
+      }
+      .bottom-line {
+        width: 100%;
+        text-align: center;
+        color: #fff;
+        font-size: 12px;
+        margin-top: 5px;
+      }
+      button:nth-child(2) {
+        height: 30px;
+      }
+    }
+  }
+}
+</style>

+ 32 - 0
src/hooks/countdown.ts

@@ -0,0 +1,32 @@
+
+import {  ref } from "vue";
+let beginTimeStrap = 1646988300000
+let endTimeStrap = 1646985600000
+function getTimeStr(timeStrap: number) {
+    let timer = new Date(timeStrap)
+    let str = `${timer.getMinutes()}:${timer.getSeconds()}`
+    return str
+
+}
+export let countdownTimer =ref(0)
+export let countdown = () => {
+    return new Promise((_resolve, _reject) => {
+     countdownTimer.value = window.setInterval(() => {
+            beginTimeStrap = beginTimeStrap - 1000
+            countdownTime.value = getTimeStr(beginTimeStrap)
+            if (beginTimeStrap == endTimeStrap) {
+                _resolve(countdownTimer.value)
+            }
+        }, 1000)
+    })
+}
+
+
+
+
+
+export let countdownTime = ref("45:00")
+
+
+
+

+ 532 - 0
src/hooks/examTest.ts

@@ -0,0 +1,532 @@
+import { ref } from "vue";
+import dayjs from "dayjs";
+import Api from '@/api/api';
+import router from "@/router/"
+import store from "@/store/"
+
+export function useExamTest() {
+    let list = ref<selectTestQuestionInfo.row[]>([
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+        {
+            createTime: '2021-11-05 10:25:51',
+            id: 20,
+            image: null,
+            imageYdt: null,
+            answer: '√',
+            answerkeyword: null,
+            explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+            explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+            explainJs:
+                '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+            explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+            issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+            opts: '√-×',
+            optsArr: ['√', '×'],
+            skillkeyword: '追究-答对',
+            titlekeyword: '追究',
+            issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+            answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+            explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+            liceCar: '1',
+            liceBus: '1',
+            liceTruck: '1',
+            liceMoto: '0',
+            sequeIssue: '2',
+            classIssue: '32',
+            placeIssue: '0',
+            excellIssue: '1',
+            copyIssue: '0',
+            mockIssue: '0',
+            sequeIssueName: '练习二',
+            placeIssueName: null,
+            excellIssueName: '必学题一',
+            classIssueName: '刑法题',
+            questionType: 1,
+            subject: 1,
+            classSort: 16,
+            excellSort: 39,
+            sequeSort: 118,
+            placeSort: null,
+            userAnswer: '',
+        },
+    ]);
+    let correctScore = ref(0)
+    let errorScore = ref(0)
+    let beforeSubmitVisible = ref(false)
+    let submitVisible = ref(false)
+
+    let submitScore = () => {
+        //重置为0,不然会有数据不准确的bug
+        correctScore.value = 0
+        errorScore.value = 0
+        list.value.forEach(item => {
+            correctScore.value += item.answer == item.userAnswer ? 1 : 0
+            if (item.userAnswer) {
+                errorScore.value += item.answer == item.userAnswer ? 0 : 1
+            }
+
+        })
+        beforeSubmitVisible.value = true
+        store.commit('SET_ENDTIME', {
+            endTime: `${dayjs().format('MM-DD HH:mm:ss')}`,
+        });
+
+
+    }
+    let listIndex = ref(0);
+
+    let changeListIndex = (e: PointerEvent) => {
+        if (e.target?.dataset?.key) {
+            listIndex.value = Number(e.target?.dataset?.key) - 1;
+        }
+    };
+    let selectExamAnswer = (item: string) => {
+        list.value[listIndex.value] = { ...list.value[listIndex.value], userAnswer: item };
+        console.log(list.value[listIndex.value], item);
+    };
+    let beforeTopic = () => {
+        if (listIndex.value == 0) {
+            return;
+        }
+        listIndex.value = listIndex.value - 1;
+    };
+    let nextTopic = () => {
+        if (listIndex.value == list.value.length - 1) {
+            return;
+        }
+        listIndex.value = listIndex.value + 1;
+    };
+
+    let getPgae = () => {
+        console.log(router)
+        let params = {
+            liceBus: "",
+            liceCar: "",
+            liceMoto: "",
+            liceTruck: "",
+            subject: Number(router.currentRoute.value.query.subject)
+        }
+        if (router.currentRoute.value.query && router.currentRoute.value.query.type) {
+            switch (router.currentRoute.value.query.type as string) {
+                case "liceBus":
+                    params.liceBus = "1"
+                    break;
+                case "liceCar":
+                    params.liceCar = "1"
+                    break;
+                case "liceMoto":
+                    params.liceMoto = "1"
+                    break;
+                case "liceTruck":
+                    params.liceTruck = "1"
+                    break;
+            }
+        }
+        Api.openApiqustionInfoSelectTestQuestionInfo(params).then((res) => {
+            res.data.rows.forEach((item) => {
+                item.optsArr = item.opts.split('-');
+                item.userAnswer = '';
+            });
+            list.value = res.data.rows;
+        });
+    }
+    return {
+        submitScore,
+        changeListIndex,
+        selectExamAnswer,
+        beforeTopic,
+        nextTopic,
+        getPgae,
+        listIndex,
+        list,
+        correctScore,
+        errorScore,
+        beforeSubmitVisible,
+        submitVisible
+
+
+
+
+
+    }
+}
+
+
+

+ 0 - 0
src/hooks/phone.ts


+ 17 - 0
src/hooks/stringMap.ts

@@ -0,0 +1,17 @@
+export function useStringMap(){
+    const subjectMap = ["科目零","科目一","科目二","科目三","科目四"]
+    const carTypeMap = {
+        "liceBus":"客车",
+        "liceCar":"小车",
+        "liceMoto":"摩托",
+        "liceTruck":"货车"
+    }
+
+    return {
+        subjectMap,
+        carTypeMap
+
+    }
+
+    
+}

+ 20 - 0
src/main.ts

@@ -0,0 +1,20 @@
+import { createApp } from 'vue';
+import App from './App';
+import router from './router';
+import store from './store';
+import '@/style/index.scss';
+import "vant/lib/index.css";
+import "babel-polyfill"
+let app = createApp(App)
+app.directive('opacity', {
+    mounted(el, binding) {
+        console.log(binding.value)
+        // binding.value 是我们传递给指令的值——在这里是 200 //透明度
+        el.style.opacity = binding.value?1:0
+    },
+    updated(el,binding){
+        el.style.opacity = binding.value?1:0
+
+    }
+})
+app.use(router).use(store).mount('#app');

+ 47 - 0
src/router/index.ts

@@ -0,0 +1,47 @@
+import { RouteRecordRaw, createRouter, createWebHistory,createWebHashHistory } from 'vue-router';
+
+const routes: RouteRecordRaw[] = [
+  {
+    path: '/',
+    name: 'Home',
+    component: () => import('../views/Home'),
+  },
+  {
+    path: '/about',
+    name: 'About',
+    component: () => import('../views/About'),
+  },
+  {
+    path: '/like',
+    name: 'Like',
+    component: () => import('../views/Like.vue'),
+  },
+  {
+    path: '/exam/begin',
+    name: 'examBegin',
+    component: () => import('../views/exam/begin.vue'),
+  },
+  {
+    path: '/exam/test',
+    name: 'examTest',
+    component: () => import('../views/exam/test.vue'),
+  },
+  {
+    path: '/exam/score',
+    name: 'examScore',
+    component: () => import('../views/exam/score.vue'),
+  },
+  {
+    path: '/aprilExam/test',
+    name: 'aprilExamTest',
+    component: () => import('../views/aprilExam/test.vue'),
+
+  }
+];
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes,
+});
+
+export default router;

+ 4 - 0
src/shim.d.ts

@@ -0,0 +1,4 @@
+declare module '*.vue' {
+  import Vue from 'vue';
+  export default Vue;
+}

+ 4 - 0
src/source.d.ts

@@ -0,0 +1,4 @@
+declare const React: string;
+declare module '*.json';
+declare module '*.png';
+declare module '*.jpg';

+ 20 - 0
src/store/index.ts

@@ -0,0 +1,20 @@
+import { state } from './state';
+import { createStore } from 'vuex';
+
+export default createStore({
+  state,
+  mutations: {
+    SET_BEGINTIME(state,payload){
+      state.beginTime =payload.beginTime
+      return state
+
+    },
+    SET_ENDTIME(state,payload){
+      state.endTime =payload.endTime
+      return state
+
+    }
+  },
+  actions: {},
+  modules: {},
+});

+ 11 - 0
src/store/state.ts

@@ -0,0 +1,11 @@
+export interface State {
+  title: string;
+  beginTime:string
+  endTime:string
+}
+
+export const state: State = {
+  title: '导航页面',
+  beginTime:"",
+  endTime:""
+};

+ 1 - 0
src/style/index.scss

@@ -0,0 +1 @@
+@import './reset.scss';

+ 22 - 0
src/style/main.scss

@@ -0,0 +1,22 @@
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+  width: 100%;
+  height: 100%;
+}
+
+#nav {
+  padding: 30px;
+
+  a {
+    font-weight: bold;
+    color: #2c3e50;
+
+    &.router-link-exact-active {
+      color: #42b983;
+    }
+  }
+}

+ 13 - 0
src/style/reset.scss

@@ -0,0 +1,13 @@
+* {
+  box-sizing: border-box;
+}
+
+html, body {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+}
+img{
+  width: 200px;
+  height: 200px;
+}

+ 70 - 0
src/utils/phone.ts

@@ -0,0 +1,70 @@
+class Phone {
+    //设备是否为android
+    isAndroid() {
+        console.log("成功进入isAndroid这个方法")
+        var u = navigator.userAgent;
+        if (u.indexOf('Android') > -1 || u.indexOf('Adr') > -1) {
+            return true;
+        }
+        return false;
+    }
+    // 判断设备为 ios
+    isIos() {
+        console.log("成功进入isIos这个方法")
+        var u = navigator.userAgent;
+        if (u.indexOf("iPhone") > -1 || u.indexOf("iOS") > -1) {
+            return true;
+        }
+        return false;
+    }
+    //获取设备的类型
+    getSysType(): string {
+        if (this.isAndroid()) {
+            return "android"
+        }
+        if (this.isIos()) {
+            return "ios"
+        }
+        return "web"
+    }
+    displayRow() {
+        if (this.isIos()) {
+            try {
+                window.webkit.messageHandlers.displayRow.postMessage('displayRow')
+            } catch (error) {
+                console.log(error)
+            }
+        } else if (this.isAndroid()) {
+            window.android&&window.android.displayRow()
+        }
+    }
+
+    displayCol(){
+        if (this.isIos()) {
+            try {
+                window.webkit.messageHandlers.displayRow.postMessage('displayCol')
+            } catch (error) {
+                console.log(error)
+            }
+        } else if (this.isAndroid()) {
+            window.android&&window.android.displayCol()
+        }
+    }
+    backView(){
+
+        if (this.isIos()) {
+            try {
+                window.webkit.messageHandlers.backView.postMessage('backView')
+            } catch (error) {
+                console.log(error)
+            }
+        } else if (this.isAndroid()) {
+            window.android&&window.android.backView()
+        }
+
+    }
+
+
+
+}
+export default Phone

+ 60 - 0
src/utils/scoreCirle.ts

@@ -0,0 +1,60 @@
+class ScoreCirle {
+  
+
+    constructor(context: CanvasRenderingContext2D, config: {
+        score: number,
+        total: number,
+        lineWidth:number
+        fillColor?:string
+        strokeColor?:string
+
+    }) {
+        this.lineWidth=config.lineWidth;
+        this.rad = (Math.PI*2)/config.total
+        this.strokeColor = config.strokeColor||"#2589fa"
+        this.context = context
+        this.score = config.score
+        this.total = config.total
+        this.width = context.canvas.width
+        this.height = context.canvas.height
+        this.radius1 = (context.canvas.width/2)-15
+        this.radius2 = (context.canvas.width/2)-30
+    }
+    total: number
+    rad:number
+    lineWidth:number
+    radius2: number
+    score: number
+    context:CanvasRenderingContext2D
+    width:number
+    height:number
+    radius1:number
+    strokeColor:string
+    init() {
+        this.context.save();
+        this.context.lineCap = "round";
+        this.context.lineWidth=this.lineWidth
+        this.context.strokeStyle=this.strokeColor
+        this.context.beginPath();
+        this.context.arc(this.width/2,this.height/2,this.radius1, -Math.PI / 2, (-Math.PI / 2)+this.score*this.rad,false);
+        this.context.moveTo(this.width/2,this.height/2);
+        this.context.textBaseline = "middle"
+        this.context.font = "45pt sans-serif";
+        this.context.textAlign="center"
+        this.context.fillStyle = "#333333"
+        this.context.fillText(`${this.score}.00`,this.width/2,this.height/2)
+        this.context.fillStyle = "#808080"
+        this.context.font = "15pt sans-serif";
+        this.context.fillText(`/${this.total}`,this.width/2,this.height/2+50)
+        this.context.stroke();
+        this.context.restore();
+
+
+
+    }
+    
+
+}
+export default ScoreCirle
+
+

+ 16 - 0
src/views/About.tsx

@@ -0,0 +1,16 @@
+import { defineComponent } from 'vue';
+import HelloWord from '../components/HelloWord';
+import Logo from '../assets/logo.png';
+
+export default defineComponent({
+  name: 'App',
+  setup() {
+    return () => (
+      <>
+        <h1>About</h1>
+        <img src={Logo}/>
+        <HelloWord/>
+      </>
+    );
+  }
+});

+ 18 - 0
src/views/Home.tsx

@@ -0,0 +1,18 @@
+import { defineComponent } from 'vue';
+import { RouterLink } from 'vue-router';
+import { useStore } from 'vuex';
+
+export default defineComponent({
+  name: 'App',
+  setup() {
+    const store = useStore()
+
+    return () => (
+      <>
+        <RouterLink to={'/exam/begin'}>前往模拟考试</RouterLink>
+        <RouterLink to={'/aprilExam/test'}>前往四月份新题</RouterLink>
+        <h1>{store.state.title}</h1>
+      </>
+    );
+  }
+});

+ 25 - 0
src/views/Like.vue

@@ -0,0 +1,25 @@
+<template>
+    <div>
+    大家好
+    <HelloWord />
+    </div>
+</template>
+
+<script  lang="ts">
+import HelloWord from '../components/HelloWord';
+import { defineComponent } from 'vue'
+export default defineComponent({
+    setup () {
+        
+
+        return {
+            HelloWord
+
+        }
+    }
+})
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 742 - 0
src/views/aprilExam/test.vue

@@ -0,0 +1,742 @@
+<template>
+  <div>
+    <div class="container" ref="container">
+      <div class="tab">
+        <div
+          v-for="(item, index) in tabItemList"
+          :key="index"
+          class="tab-item"
+          @click="
+            () => {
+              tabItemListIndex = index;
+              listIndex = listIndexList[tabItemListIndex];
+              getPage(index);
+            }
+          "
+          :class="{
+            'tab-item_select': tabItemListIndex == index,
+          }"
+        >
+          <img class="tab-img" v-if="tabItemListIndex !== index" :src="item.whiteImg" />
+          <img class="tab-img" v-if="tabItemListIndex === index" :src="item.blueImg" />
+          <span class="tabItem-title">{{ item.title }}</span>
+        </div>
+      </div>
+      <div class="topics">
+        <div class="topics-title">{{ list[listIndex].type == 1 ? '判断' : '选择' }}</div>
+        <div class="topics-content">
+          {{ list[listIndex].name }}
+        </div>
+        <div class="image">
+          <img v-if="list[listIndex].image" :src="list[listIndex].image" />
+        </div>
+      </div>
+      <div class="options">
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 1)"
+          v-if="list[listIndex].an1"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 1 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 1
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 1 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 1
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">A</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 1 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 1,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 1 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 1,
+              }"
+            >
+              {{ list[listIndex].an1 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an1 }}</div>
+        </div>
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 2)"
+          v-if="list[listIndex].an2"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 2 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 2
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 2 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 2
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">B</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 2 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 2,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 2 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 2,
+              }"
+            >
+              {{ list[listIndex].an2 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an2 }}</div>
+        </div>
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 3)"
+          v-if="list[listIndex].an3"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 3 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 3
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 3 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 3
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">C</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 3 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 3,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 3 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 3,
+              }"
+            >
+              {{ list[listIndex].an3 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an3 }}</div>
+        </div>
+
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 4)"
+          v-if="list[listIndex].an4"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 4 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 4
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 4 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 4
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">D</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 4 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 4,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 4 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 4,
+              }"
+            >
+              {{ list[listIndex].an4 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an4 }}</div>
+        </div>
+
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 5)"
+          v-if="list[listIndex].an5"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 5 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 5
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 5 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 5
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">E</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 5 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 5,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 5 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 5,
+              }"
+            >
+              {{ list[listIndex].an5 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an5 }}</div>
+        </div>
+
+        <div
+          class="options-select"
+          @click="selectAnswer(list[listIndex], 6)"
+          v-if="list[listIndex].an6"
+        >
+          <div class="options-selectText">
+            <img
+              src="@/assets/img/okimg.png"
+              v-if="
+                list[listIndex].userAnswer == 6 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer == 6
+              "
+              class="options-selectText-okimg"
+            />
+            <img
+              src="@/assets/img/noimg.png"
+              v-if="
+                list[listIndex].userAnswer == 6 &&
+                list[listIndex].isExplainJs &&
+                list[listIndex].rightAnswer !== 6
+              "
+              class="options-selectText-noimg"
+            />
+            <div class="options-selectText-header">F</div>
+            <div
+              class="options-selectText-mid"
+              :class="{
+                'options-selectText-mid_selectOk':
+                  list[listIndex].userAnswer == 6 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer == 6,
+                'options-selectText-mid_selectNo':
+                  list[listIndex].userAnswer == 6 &&
+                  list[listIndex].isExplainJs &&
+                  list[listIndex].rightAnswer !== 6,
+              }"
+            >
+              {{ list[listIndex].an6 }}
+            </div>
+          </div>
+          <div class="options-bg">{{ list[listIndex].an6 }}</div>
+        </div>
+      </div>
+
+      <div class="explain" v-opacity="list[listIndex].isExplainJs">
+        <div class="explain-answer">
+          <div class="explain-answerText">
+            <span>正确答案:</span>
+            <span style="color: #12af85">{{ answerIndexMap[list[listIndex].rightAnswer] }}</span>
+          </div>
+        </div>
+        <div class="explain-header">题目解析</div>
+        <div class="explain-content">
+          {{ list[listIndex].explainJs }}
+        </div>
+      </div>
+      <div class="bottom">
+        <div class="bottom-container">
+          <img @click="backTopics" src="@/assets/img/beforeTopics.png" />
+          <span>
+            <span style="color: #ffac4d">{{ listIndex + 1 }}</span
+            >/{{ list.length }}
+          </span>
+          <img @click="nextTopics" src="@/assets/img/nextTopics.png" />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, onUnmounted } from 'vue';
+import { Toast } from 'vant';
+import api from '@/api/api';
+import blueTabItemImg0 from '@/assets/img/blueCar.png';
+import blueTabItemImg1 from '@/assets/img/blueBus.png';
+import blueTabItemImg2 from '@/assets/img/blueTruck.png';
+import blueTabItemImg3 from '@/assets/img/blueMoto.png';
+import whiteTabItemImg0 from '@/assets/img/whiteCar.png';
+import whiteTabItemImg1 from '@/assets/img/whiteBus.png';
+import whiteTabItemImg2 from '@/assets/img/whiteTruck.png';
+import whiteTabItemImg3 from '@/assets/img/whiteMoto.png';
+import { useRoute } from 'vue-router';
+const tabItemListIndex = ref(0);
+const answerIndexMap = ['!', 'A', 'B', 'C', 'D', 'E', 'F', 'G'];
+const tabItemList = [
+  {
+    blueImg: blueTabItemImg0,
+    whiteImg: whiteTabItemImg0,
+    title: '小车',
+  },
+  {
+    blueImg: blueTabItemImg1,
+    whiteImg: whiteTabItemImg1,
+    title: '客车',
+  },
+  {
+    blueImg: blueTabItemImg2,
+    whiteImg: whiteTabItemImg2,
+    title: '货车',
+  },
+  {
+    blueImg: blueTabItemImg3,
+    whiteImg: whiteTabItemImg3,
+    title: '摩托车',
+  },
+];
+let list = ref<questionTwoList.row[]>([
+  {
+    an1: '正确',
+    an2: '错误',
+    an3: '',
+    an4: '',
+    an5: '',
+    an6: '',
+    an7: '',
+    cartype: '',
+    name: '机动车驾驶人在实习期内有记满12分记录的,注销其实习的准驾车型驾驶资格。',
+    explainJs:
+      '《机动车驾驶证申请和使用规定》第八十条:机动车驾驶人在实习期内发生的道路交通安全违法行为被记满12分的,注销其实习的准驾车型驾驶资格',
+    id: 0,
+    image: '',
+    kemu: 0,
+    name: '',
+    num: 0,
+    rightAnswer: 0,
+    type: 0,
+  },
+]);
+let listIndexList = [0, 0, 0, 0];
+let timer = window.setInterval(() => {
+  listIndexList[tabItemListIndex.value] = listIndex.value;
+}, 400);
+onUnmounted(() => {
+  window.clearInterval(timer);
+});
+let listIndex = ref(0);
+let list0 = ref<questionTwoList.row[]>([]);
+let list1 = ref<questionTwoList.row[]>([]);
+let list2 = ref<questionTwoList.row[]>([]);
+let list3 = ref<questionTwoList.row[]>([]);
+let container = ref(null);
+const query = {
+  pageNum: 1,
+  pageSize: 1000,
+  cartype: '0' as '0' | '1' | '2' | '3',
+  kemu: 1,
+};
+let getPage = (index: number) => {
+  let indexStr = String(index);
+  query.cartype = indexStr as '0' | '1' | '2' | '3';
+  switch (index) {
+    case 0:
+      if (list0.value.length == 0) {
+        api.questionTwoList(query).then((res) => {
+          list0.value = res.data.rows;
+          list.value = res.data.rows;
+        });
+      } else {
+        list.value = list0.value;
+      }
+
+      break;
+    case 1:
+      if (list1.value.length == 0) {
+        api.questionTwoList(query).then((res) => {
+          list1.value = res.data.rows;
+          list.value = res.data.rows;
+        });
+      } else {
+        list.value = list1.value;
+      }
+
+      break;
+    case 2:
+      if (list2.value.length == 0) {
+        api.questionTwoList(query).then((res) => {
+          list2.value = res.data.rows;
+          list.value = res.data.rows;
+        });
+      } else {
+        list.value = list2.value;
+      }
+      break;
+    case 3:
+      if (list3.value.length == 0) {
+        api.questionTwoList(query).then((res) => {
+          list3.value = res.data.rows;
+          list.value = res.data.rows;
+        });
+      } else {
+        list.value = list3.value;
+      }
+
+      break;
+  }
+};
+let nextTopics = (e?: Event) => {
+  e?.preventDefault();
+  if (listIndex.value == list.value.length - 1) {
+    Toast('到底了');
+    return;
+  } else {
+    listIndex.value = listIndex.value + 1;
+  }
+  container.value.scrollTo(0, 0);
+  // container.scrollTo(0, 0);
+};
+let backTopics = (e?: Event) => {
+  e?.preventDefault();
+  if (listIndex.value == 0) {
+    Toast('到底了');
+    return;
+  } else {
+    listIndex.value = listIndex.value - 1;
+  }
+  container.value.scrollTo(0, 0);
+};
+let selectAnswer = (item: questionTwoList.row, index: number) => {
+  if (!item.isExplainJs) {
+    item.isExplainJs = true;
+    item.userAnswer = index;
+    if (item.userAnswer == item.rightAnswer) {
+      window.setTimeout(() => {
+        listIndex.value = listIndex.value + 1;
+      }, 1200);
+    }
+  }
+};
+
+let slideTopics = (() => {
+  let touchList0: TouchList;
+  let beginTimeStrap: number = 0;
+
+  return (e: TouchEvent) => {
+    e.preventDefault();
+    let currentTimeStrap: number = +new Date();
+    let touchList1: TouchList;
+    if (currentTimeStrap - beginTimeStrap < 1500) {
+      touchList1 = e.changedTouches;
+      //单点触摸的时候
+      if (touchList0.length == 1 && touchList1.length == 1) {
+        console.log(touchList1[0].clientX - touchList0[0].clientX);
+        if (touchList1[0].clientX - touchList0[0].clientX > 200) {
+          backTopics();
+          touchList0 = e.changedTouches;
+        } else if (touchList1[0].clientX - touchList0[0].clientX < -200) {
+          nextTopics();
+          touchList0 = e.changedTouches;
+        }
+      }
+    } else {
+      beginTimeStrap = currentTimeStrap;
+      touchList0 = e.changedTouches;
+    }
+  };
+})();
+//初始获取题目
+getPage(0);
+//获取url参数来跳转题目
+const route = useRoute();
+document.title = "四月份新题"
+//truck
+</script>
+
+<style lang="scss" scoped>
+.container {
+  width: 100vw;
+  height: 100vh;
+  background-image: url('./../../assets/img/aprilExam.png');
+  background-size: 100%;
+  padding-bottom: 136px;
+  overflow-y: scroll;
+}
+.tab {
+  padding: 0px 60px;
+  padding-top: 32px;
+  display: flex;
+  justify-content: space-around;
+
+  .tab-item {
+    display: flex;
+
+    align-content: center;
+    align-items: center;
+    width: 140px;
+    height: 60px;
+    padding: 0px 15px;
+    padding-left: 10px;
+    justify-content: space-between;
+    white-space: nowrap;
+    color: #fff;
+    .tabItem-title {
+      font-size: 26px;
+    }
+  }
+  .tab-item_select {
+    background: #fff;
+    border: 4px solid #2d2d2d;
+    border-radius: 35px;
+    color: #498ef5;
+  }
+  .tab-img {
+    width: 40px;
+    height: 24px;
+  }
+}
+.topics {
+  width: 690px;
+  margin: 0 auto;
+  margin-top: 154px;
+  border: 5px solid #2d2d2d;
+  padding: 26px 0;
+  position: relative;
+  background: #fff;
+  border-radius: 8px;
+  .topics-title::before {
+    color: white;
+  }
+  .topics-title {
+    position: absolute;
+    top: 26px;
+    width: 88px;
+    height: 44px;
+    left: -5px;
+    background: #01c18d;
+    border: 5px solid #333;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+  }
+  .topics-content {
+    text-align: left;
+    font-size: 30px;
+    text-indent: 3em;
+    font-weight: 600;
+    line-height: 54px;
+  }
+}
+.image {
+  img {
+    width: 640px;
+  }
+}
+.options {
+  width: 100%;
+  margin-top: 30px;
+  padding-left: 60px;
+  .options-select {
+    width: 644px;
+    position: relative;
+    margin-bottom: 26px;
+    overflow: hidden;
+  }
+  .options-selectText {
+    border: 5px solid #2d2d2d;
+
+    text-align: left;
+    display: flex;
+    align-content: center;
+    align-items: center;
+    background: #fff;
+    border-radius: 8px;
+    position: absolute;
+    left: 0;
+    top: 0;
+
+    width: 630px;
+    z-index: 10;
+  }
+  .options-selectText-header {
+    background: #ffac4d;
+    width: 56px;
+    height: 56px;
+    border: 3px solid #2d2d2d;
+    position: relative;
+    left: -3px;
+    display: flex;
+    justify-content: center;
+    align-content: center;
+    align-items: center;
+    color: white;
+    border-radius: 8px;
+    flex-basis: 1;
+    flex-shrink: 0;
+    position: absolute;
+    left: -4px;
+    top: 14px;
+  }
+
+  .options-selectText-mid {
+    padding-left: 75px;
+    padding-right: 72px;
+    padding-top: 22px;
+    padding-bottom: 22px;
+    font-size: 30px;
+  }
+  .options-selectText-mid_selectOk {
+    color: #12af85;
+  }
+  .options-selectText-mid_selectNo {
+    color: #ff4d53;
+  }
+  .options-selectText-okimg {
+    position: absolute;
+    right: 28px;
+    width: 28px;
+    height: 28px;
+  }
+  .options-selectText-noimg {
+    position: absolute;
+    right: 28px;
+    width: 28px;
+    height: 28px;
+  }
+  .options-bg {
+    width: 630px;
+    background: #d6b352;
+    border: 5px solid #2d2d2d;
+    padding: 22px 0;
+    margin-left: 14px;
+    margin-top: 14px;
+    border-radius: 8px;
+    color: #d6b352;
+    padding-left: 75px;
+    padding-right: 72px;
+    padding-top: 22px;
+    padding-bottom: 22px;
+    font-size: 30px;
+  }
+}
+.explain {
+  margin-top: 60px;
+  width: 690px;
+  border: 4px solid #2d2d2d;
+  margin: 0 auto;
+  background: #fff;
+  padding-bottom: 20px;
+  .explain-answer {
+    height: 86px;
+    display: flex;
+    align-items: flex-end;
+    font-size: 32px;
+    font-weight: 700;
+    .explain-answerText {
+      width: 630px;
+      margin: 0 auto;
+      text-align: left;
+      border-bottom: 2px solid #cccccc;
+    }
+  }
+  .explain-header {
+    margin-top: 32px;
+    text-align: left;
+    padding-left: 30px;
+    font-weight: 600;
+  }
+  .explain-content {
+    font-size: 24px;
+    text-align: left;
+    padding: 8px 30px;
+  }
+}
+.bottom {
+  position: fixed;
+  bottom: 0;
+  width: 100%;
+  height: 136px;
+  background: #fff;
+  display: flex;
+  align-content: center;
+  align-items: center;
+  padding: 0 60px;
+  z-index: 100;
+  .bottom-container {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    height: 60px;
+    line-height: 60px;
+    font-weight: 700;
+    img {
+      width: 60px;
+      height: 60px;
+    }
+  }
+}
+</style>

+ 235 - 0
src/views/exam/begin.vue

@@ -0,0 +1,235 @@
+<template>
+  <div class="container">
+    <img @click="backView" src="@/assets/img/backButton.png" class="backButton" />
+    <div style="background: #f4f3ef">
+      <div class="header-text-left">Sub of a Driver Training System</div>
+      <div class="headerImg"><img :src="subjectImg" /></div>
+      <div class="header-text-right">Sub of a Driver Training System</div>
+      <div style="background: #fff; width: 100%; overflow: hidden" class="">
+        <div class="content">
+          <div class="box1">
+            <div class="info">考试须知:</div>
+            <div>1.遵守考场纪律,服从监考人员指挥。</div>
+            <div>2.进入考场、手机关机。禁止抽烟,禁止吃零食。</div>
+            <div>3.未经工作人员允许,考生禁止随意出入考场。</div>
+            <div>4.考场内禁止大声题哗,禁止随意走动。</div>
+            <div>5.考试中认真答题,不准交头接耳。</div>
+            <div>6.考试中不准冒名顶替,不准弄虚作假。</div>
+            <div>7.注意考场卫生,禁止随地吐痰,禁止乱扔纸屑。</div>
+            <div>8.爱护公物及考试设备。</div>
+          </div>
+          <div class="box2">
+            <div class="info">驾考理论考试01号考台:</div>
+            <div style="color: #ba2f35" class="box2-id">身份证号:123456789101112131415161718</div>
+            <div class="box2-space1"></div>
+            <div class="box2-name" style="color: #ba2f35">
+              考生姓名:{{ query.username || '张三' }}
+            </div>
+            <div class="pl124 exam-container">
+              <div
+                @click="
+                  () => {
+                    push({
+                      name: 'examTest',
+                      query: query,
+                    });
+                  }
+                "
+                class="exam-button"
+              >
+                开始考试
+              </div>
+            </div>
+            <div class="mt14" style="color: #ba2f35">点击"确认"按钮开始考试</div>
+            <div class="box2-tip" style="color: #666769">
+              操作提示:每题考试答案确认后,点击【下一题】,电脑立即判定所选答案,如选择错误,系统将提示正确答案,提示后不允许修改答案。
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+// import Api from '@/api/api';
+import Phone from '@/utils/phone';
+import subject4Img from '@/assets/img/subject4@2x.png';
+import subject1Img from '@/assets/img/subject1@2x.png';
+export default defineComponent({
+  setup() {
+    // window.localStorage.setItem(
+    //   'token',
+    //   'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjllNTU3NDk1LWQyOGQtNDMyNy1hYTBjLTdjOTk2OTk5NTk1YiJ9.GvGgys5erlFYJMLEhk2S1e7W_9CKDJohfXfW8fZpitkpZ8RWni5u9VvTGjl8dhD1916s-4MTM_7TsNaTNwsuPg',
+    // );
+    //旋转你的屏幕
+    const phone = new Phone();
+    const backView = phone.backView.bind(phone);
+    const push = useRouter().push;
+    const route = useRoute();
+    const query = route.query;
+    const subjectImg = query.subject == '1' ? subject1Img : subject4Img;
+    console.log(query);
+
+    if (query.token) {
+      window.localStorage.setItem('token', query.token as string);
+    }
+    // Api.loginCodeTest().then(res=>{
+    //   console.log(res)
+    //   window.localStorage.setItem("token",res.data.token)
+    // })
+
+    return {
+      query,
+      push,
+      subjectImg,
+      backView,
+    };
+  },
+});
+</script>
+
+<style lang="scss" scoped>
+.font11 {
+  font-size: 11px;
+}
+.mt14 {
+  margin-top: 14px;
+}
+.pl138 {
+  padding-left: 138px;
+}
+.pl124 {
+  padding-left: 124px;
+}
+.pl24 {
+  padding-left: 24px;
+}
+.exam-container {
+  margin-top: 18px;
+  .exam-button {
+    width: 106px;
+    height: 27px;
+    background: #fff;
+    opacity: 1;
+    line-height: 27px;
+    color: #38393b;
+    text-align: center;
+    border-radius: 4px;
+    font-size: 16px;
+    border: 1px solid #a1a2a4;
+    border-radius: 0;
+  }
+}
+
+.container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  background: #fff;
+  position: relative;
+  .backButton {
+    position: absolute;
+    top: 28px;
+    left: 13px;
+    width: 22px;
+    height: 22px;
+  }
+}
+.header {
+  width: 100%;
+  height: 21.8vh;
+  background: linear-gradient(180deg, #498ef5 0%, #4da8e6 100%);
+  font-size: 26px;
+  line-height: 21.8vh;
+  color: white;
+}
+.header-text-left {
+  color: #a9a9a1;
+  text-align: left;
+  height: 13px;
+  line-height: 13px;
+  font-size: 8px;
+}
+.header-text-right {
+  color: #a9a9a1;
+  text-align: right;
+  height: 13px;
+  font-size: 8px;
+  border-bottom: 1px solid #d8d7d3;
+  padding-right: 15px;
+}
+.headerImg {
+  width: 100%;
+  height: auto;
+
+  > img {
+    width: 100%;
+    height: auto;
+  }
+}
+.mt15 {
+  margin-top: 15px;
+}
+.content {
+  width: 723px;
+  height: 67vh;
+  padding: 0 0;
+  margin: 0 auto;
+  margin-top: 16px;
+  display: flex;
+  flex-wrap: wrap;
+  border: 1px solid #eae9e5;
+  background: #f5f6f8;
+  .box1 {
+    width: 50%;
+    height: 100%;
+    padding-top: 15px;
+    padding-left: 20px;
+    color: #3d3c38;
+    border-right: 1px solid #eae9e5;
+    .info {
+      font-size: 14px;
+      margin-bottom: 7px;
+      font-weight: 700;
+      font-size: 14px;
+    }
+    div {
+      text-align: left;
+      font-size: 13px;
+      margin-bottom: 4px;
+    }
+  }
+  .box2 {
+    width: 50%;
+    height: 100%;
+    text-align: left;
+    padding-top: 18px;
+    padding-left: 17px;
+    font-size: 13px;
+    .box2-space1 {
+      height: 5px;
+    }
+    .box2-id {
+      font-weight: 700;
+      font-size: 13px;
+    }
+    .box2-name {
+      font-weight: 700;
+      font-size: 13px;
+    }
+    .box2-tip {
+      margin-top: 7px;
+      font-size: 11px;
+    }
+    .info {
+      margin-bottom: 7px;
+      font-size: 14px;
+      font-weight: 700;
+    }
+  }
+}
+</style>

+ 98 - 0
src/views/exam/score.vue

@@ -0,0 +1,98 @@
+<template>
+  <div>
+    <div class="header">
+      <span> 测试时间:</span>
+      <span>{{ store.state.beginTime }}</span>
+      <span>~</span>
+      <span>{{ store.state.endTime }}</span>
+    </div>
+    <div class="score">
+      <div class="button1">得分</div>
+      <canvas width="250" height="250" id="score"></canvas>
+    </div>
+    
+    <div @click="backView" class="bottom-button">
+      <button>结束考试</button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted } from 'vue';
+import Phone from '@/utils/phone';
+import { useStore } from 'vuex';
+import ScoreCirle from "@/utils/scoreCirle"
+import { useRoute } from 'vue-router';
+export default defineComponent({
+  setup() {
+    const store = useStore();
+    const query = useRoute().query
+    onMounted(() => {
+      const c: HTMLCanvasElement = document.getElementById('score') as HTMLCanvasElement;
+      const context: CanvasRenderingContext2D = c.getContext('2d') as CanvasRenderingContext2D;
+        let scoreCirle = new ScoreCirle(context,{
+            score:Number(query.score),
+            total:Number(query.total),
+            lineWidth:12
+        })
+        scoreCirle.init()
+    });
+
+    const phone = new Phone();
+    //解决this指向的问题
+    let backView = phone.backView.bind(phone)
+    // phone.displayCol();
+    return {
+      store,
+      backView
+      
+    
+    };
+  },
+});
+</script>
+
+<style lang="scss" scoped>
+.header {
+  font-size: 24px;
+  line-height: 64px;
+  height: 64px;
+  border-bottom: 1px solid #d8d8d8;
+}
+.bottom-button{
+
+  bottom: 0;
+  margin-top: 25px;
+  button{
+    border: 0;
+    color: #fff;
+    font-size: 35px;
+    background: #2589fa;
+    line-height: 100px;
+    width: 250px;
+    height: 100px;
+    border-radius: 44px;
+
+  }
+  
+}
+.score {
+  margin-top: 40px;
+  display: flex;
+  flex-flow: column wrap;
+  align-content: center;
+  align-items: center;
+  #score{
+    margin-top: 25px;
+  }
+  .button1 {
+    width: 300px;
+    height: 70px;
+    border-radius: 24px;
+    overflow: hidden;
+    color: #fff;
+    line-height: 70px;
+    background-image: linear-gradient(to bottom right, rgb(112, 170, 252), #2589fa);
+  }
+}
+</style>

+ 1160 - 0
src/views/exam/test.vue

@@ -0,0 +1,1160 @@
+<template>
+  <div class="center">
+    <beforeSubmitMask
+      :show="beforeSubmitVisible"
+      @cancel="
+        () => {
+          beforeSubmitVisible = false;
+        }
+      "
+      :correctScore="correctScore"
+      :errorScore="errorScore"
+      :total="list.length"
+      @confirm="
+        () => {
+          beforeSubmitVisible = false;
+          submitVisible = true;
+        }
+      "
+    ></beforeSubmitMask>
+    <submitMask v-if="submitVisible" :correctScore="correctScore"></submitMask>
+    <div class="box">
+      <div class="main">
+        <div class="container1">
+          <div class="kaoti">
+            <div class="kaoti-header">金牌驾考</div>
+            <div class="kaoti-headerText">第01考台</div>
+          </div>
+          <div class="student">
+            <div class="student-header">考生信息</div>
+            <div class="mt10">
+              <img class="driver" :src="headimg" />
+              <div class="driverInfo">
+                <div>姓名:{{ username }}</div>
+                <div>性别:{{ sex }}</div>
+                <div>类型:{{ carType }}</div>
+                <div>科目:{{ subject }}</div>
+              </div>
+            </div>
+          </div>
+          <div class="restTime">
+            <div class="restTime-header">剩余时间</div>
+            <div class="restTime-headerText">{{ countdownTime }}</div>
+          </div>
+        </div>
+        <div class="container2">
+          <div class="container2-row1">
+            <div class="topic">
+              <div class="topic-header">考试题目</div>
+              <div class="topic-issue">
+                <span>{{ listIndex + 1 }}.{{ list[listIndex].issue }}</span>
+              </div>
+              <div class="mt9 topic-select">
+                <div
+                  v-for="(item, index) in list[listIndex].optsArr"
+                  :key="index"
+                  class="topic-select-item"
+                >
+                  {{ letter[index] }}.<span>{{ item }}</span>
+                </div>
+                <!-- <div class="topic-select-item">B.<span>xx</span></div> countdown-->
+              </div>
+            </div>
+            <div class="answer">
+              <span class="answer-tip">您选择的答案:</span>
+              <div style="display: flex; align-items: center">
+                <span>选项:</span>
+                <div class="answer-list">
+                  <div
+                    v-for="(item, index) in list[listIndex].optsArr"
+                    :key="index"
+                    class="answer-list-item"
+                    @click="selectExamAnswer(item)"
+                    :class="{
+                      answer_selected: list[listIndex].userAnswer == item,
+                    }"
+                  >
+                    {{ letter[index] }}
+                  </div>
+                  <!-- <div class="answer-list-item answer-list-item_select">B</div> -->
+                </div>
+              </div>
+            </div>
+            <div class="coll">
+              <table border="0" class="coll-table-topic">
+                <tr :key="index" v-for="(item, index) in list.length / 10">
+                  <td
+                    v-for="(_item, _index) in 10"
+                    :class="{
+                      collselected: index * 10 + _index == listIndex,
+                    }"
+                    :data-key="index * 10 + _index + 1"
+                    :key="_index"
+                    @click="changeListIndex"
+                  >
+                    <div
+                      style="white-space: nowrap"
+                      :data-key="index * 10 + _index + 1"
+                      class="coll-table-topic-item"
+                    >
+                      {{ index * 10 + _index + 1 }}
+                    </div>
+                  </td>
+                </tr>
+              </table>
+            </div>
+          </div>
+          <div class="container2-row2">
+            <div class="w-half">
+              <div class="left-line1">操作提示:判断题</div>
+              <div class="left-line2">请在备选答案中选择你认为正确的答案!</div>
+            </div>
+            <div class="w-half">
+              <div class="right">
+                <div
+                  @click="beforeTopic"
+                  class="right-button1"
+                  :class="{
+                    button1_disabled: listIndex == 0,
+                  }"
+                >
+                  上一题
+                </div>
+                <div
+                  @click="nextTopic"
+                  class="right-button1"
+                  :class="{
+                    button1_disabled: listIndex == list.length - 1,
+                  }"
+                >
+                  下一题
+                </div>
+                <div @click="submitScore" class="right-button2">交卷</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- <div class="container3">
+          <div class="coll">
+            <table border="0" class="coll-table-topic">
+              <tr>
+                <td class="coll-table-topic-item bg-498ef5">
+                  <div class="coll-table-topic-item coll-header1-row bg-498ef5">题目</div>
+                </td>
+                <td
+                  class="coll-table-topic-item bg-498ef5"
+                  v-for="(item, index) in 10"
+                  :key="index"
+                >
+                  <div class="coll-table-topic-item coll-header1-row bg-498ef5">
+                    {{ index + 1 }}列
+                  </div>
+                </td>
+              </tr>
+
+              <tr :key="index" v-for="(item, index) in list.length / 10">
+                <td class="coll-table-topic-item bg-498ef5">
+                  <div style="white-space: nowrap">{{ index + 1 }}行</div>
+                </td>
+                <td
+                  v-for="(_item, _index) in 10"
+                  :class="{
+                    collselected: index * 10 + _index == listIndex,
+                  }"
+                  :data-key="index * 10 + _index + 1"
+                  :key="_index"
+                  @click="changeListIndex"
+                >
+                  <div
+                    style="white-space: nowrap"
+                    :data-key="index * 10 + _index + 1"
+                    class="coll-table-topic-item"
+                  >
+                    {{ index * 10 + _index + 1 }}
+                  </div>
+                </td>
+              </tr>
+            </table>
+          </div>
+        </div> -->
+      </div>
+      <div class="bottom">
+        <img
+          class="icon"
+          @click="
+            () => {
+              router.back();
+            }
+          "
+          src="@/assets/img/backButton.png"
+        />
+        <img class="answer-img" v-if="list[listIndex].image" :src="list[listIndex].image" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, ref, onMounted } from 'vue';
+import dayjs from 'dayjs';
+import beforeSubmitMask from '@/components/mask/beforeSubmitMask.vue';
+import submitMask from '@/components/mask/submitMask.vue';
+// import Api from '@/api/api';
+import { countdownTime, countdown, countdownTimer } from '@/hooks/countdown';
+import { useStore } from 'vuex';
+// import router from '@/router';
+import { useExamTest } from '@/hooks/examTest';
+import { useRoute, useRouter } from 'vue-router';
+import { useStringMap } from '@/hooks/stringMap';
+export default defineComponent({
+  setup() {
+    //清除定时器
+    //
+    window.clearInterval(countdownTimer.value);
+    const headimg = useRoute().query?.headimg;
+    const username = useRoute().query?.username;
+    const store = useStore();
+    const router = useRouter();
+
+    const { subjectMap, carTypeMap } = useStringMap();
+    let subject = subjectMap[Number(useRoute().query?.subject)];
+    let sex = '男';
+    let carType = carTypeMap[useRoute().query?.type as string];
+    let {
+      list,
+      beforeSubmitVisible,
+      listIndex,
+      submitScore,
+      changeListIndex,
+      selectExamAnswer,
+      beforeTopic,
+      nextTopic,
+      getPgae,
+      correctScore,
+      errorScore,
+      submitVisible,
+    } = useExamTest();
+    //设置开始时间
+    store.commit('SET_BEGINTIME', {
+      beginTime: `${dayjs().format('MM-DD HH:mm:ss')}`,
+    });
+    getPgae();
+
+    // let list = ref<selectTestQuestionInfo.row[]>([
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    //   {
+    //     createTime: '2021-11-05 10:25:51',
+    //     id: 20,
+    //     image: null,
+    //     imageYdt: null,
+    //     answer: '√',
+    //     answerkeyword: null,
+    //     explainGif: 'https://t1-1305573081.file.myqcloud.com/qb/gif/20.gif',
+    //     explainJq: '题目中看到“追究”.答对;“不追究”.答错。',
+    //     explainJs:
+    //       '《道路交通安全法》第九十九条:未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的,由公安机关交通管理部门处二百元以上二千元以下罚款,可以并处十五日以下拘留。',
+    //     explainMp3: 'https://t1-1305573081.file.myqcloud.com/qb/mp3/explain20.mp3',
+    //     issue: '对未取得驾驶证驾驶机动车的,追究其法律责任。',
+    //     opts: '√-×',
+    //     optsArr: ['√', '×'],
+    //     skillkeyword: '追究-答对',
+    //     titlekeyword: '追究',
+    //     issuemp3: 'https://t1-1305573081.file.myqcloud.com/qb/issue/issue20.mp3',
+    //     answermp3: 'https://t1-1305573081.file.myqcloud.com/qb/answer/answer20.mp3',
+    //     explainjsmp3: 'https://t1-1305573081.file.myqcloud.com/qb/explainjs/explainJS20.mp3',
+    //     liceCar: '1',
+    //     liceBus: '1',
+    //     liceTruck: '1',
+    //     liceMoto: '0',
+    //     sequeIssue: '2',
+    //     classIssue: '32',
+    //     placeIssue: '0',
+    //     excellIssue: '1',
+    //     copyIssue: '0',
+    //     mockIssue: '0',
+    //     sequeIssueName: '练习二',
+    //     placeIssueName: null,
+    //     excellIssueName: '必学题一',
+    //     classIssueName: '刑法题',
+    //     questionType: 1,
+    //     subject: 1,
+    //     classSort: 16,
+    //     excellSort: 39,
+    //     sequeSort: 118,
+    //     placeSort: null,
+    //     userAnswer: '',
+    //   },
+    // ]);
+
+    // let listIndex = ref(0);
+    // let submitScore = () => {
+    //   let score =0
+    //   list.value.forEach(item=>{
+    //     score +=item.answer==item.userAnswer?1:0
+    //   })
+    //   store.commit('SET_ENDTIME', {
+    //     endTime: `${dayjs().format('MM-DD HH:mm:ss')}`,
+    //   });
+    //   router.push({
+    //     name: 'examScore',
+    //     query: {
+    //       score:score,
+    //       total:list.value.length
+    //     },
+    //   });
+    // };
+    // let changeListIndex = (e: PointerEvent) => {
+    //   listIndex.value = Number(e.target?.dataset?.key) - 1;
+    // };
+    // let selectExamAnswer = (item: string) => {
+    //   list.value[listIndex.value] = { ...list.value[listIndex.value], userAnswer: item };
+    //   console.log(list.value[listIndex.value], item);
+    // };
+    // let beforeTopic = () => {
+    //   if (listIndex.value == 0) {
+    //     return;
+    //   }
+    //   listIndex.value = listIndex.value - 1;
+    // };
+    // let nextTopic = () => {
+    //   if (listIndex.value == list.value.length - 1) {
+    //     return;
+    //   }
+    //   listIndex.value = listIndex.value + 1;
+    // };
+
+    // Api.studentQustionInfoSelectTestQuestionInfo({
+    //   liceBus: '',
+    //   liceCar: '1',
+    //   liceMoto: '',
+    //   liceTruck: '',
+    //   subject: 1,
+    // }).then((res) => {
+    //   res.data.rows.forEach((item) => {
+    //     item.optsArr = item.opts.split('-');
+    //     item.userAnswer = '';
+    //   });
+    //   list.value = res.data.rows;
+    // });
+
+    onMounted(() => {
+      //倒计时结束
+      countdown().then((timer) => {
+        window.clearInterval(timer as number);
+        submitScore();
+      });
+    });
+
+    return {
+      list,
+      letter: ['A', 'B', 'C', 'D', 'E', 'F'],
+      listIndex: listIndex,
+      beforeTopic,
+      nextTopic,
+      changeListIndex,
+      selectExamAnswer,
+      countdownTime,
+      store,
+      router,
+      submitScore,
+      beforeSubmitVisible,
+      submitVisible,
+      correctScore,
+      errorScore,
+      headimg,
+      subject,
+      username,
+      carType,
+      sex,
+    };
+  },
+  components: {
+    beforeSubmitMask,
+    submitMask,
+  },
+});
+</script>
+
+<style lang="scss" scoped>
+.w-half {
+  width: 50%;
+}
+.mt10 {
+  margin-top: 10px;
+}
+.mt6 {
+  margin-top: 6px;
+}
+.mt13 {
+  margin-top: 13px;
+}
+.border-none {
+  border: none;
+}
+.bg-498ef5 {
+  background: #498ef5;
+}
+.bottom {
+  display: flex;
+  position: relative;
+  justify-content: center;
+  border-left: 1px solid #c6c7c9;
+  border-bottom: 1px solid #c6c7c9;
+  border-right: 1px solid #c6c7c9;
+  height: 74px;
+  .icon {
+    width: 35px;
+    height: 35px;
+    position: absolute;
+    left: 17px;
+    bottom: 21px;
+  }
+}
+.center {
+  display: flex;
+
+  height: 100vh;
+  background: #f6f2f1;
+}
+.box {
+  padding-top: 5px;
+  padding-bottom: 5px;
+  padding-left: 7px;
+  padding-right: 7px;
+  overflow: hidden;
+}
+
+.main {
+  display: flex;
+  border: 1px solid #c6c7c9;
+}
+.container3 {
+  flex: 1;
+}
+.container2 {
+  box-sizing: border-box;
+  width: 613px;
+  .container2-row1 {
+    display: flex;
+    flex-wrap: wrap;
+    flex-direction: column;
+    height: 220px;
+    width: 100%;
+  }
+  .container2-row2 {
+    display: flex;
+    flex-wrap: wrap;
+    flex-direction: row;
+    height: 42px;
+    width: 100%;
+    align-content: center;
+    align-items: center;
+    border-top: 1px solid #b8c0cc;
+    border-left: 1px solid #b8c0cc;
+    text-align: left;
+    font-size: 11px;
+    padding-left: 8px;
+    .left-line1 {
+      color: #bc322d;
+      text-align: left;
+      font-size: 11px;
+      width: 100%;
+    }
+    .left-line2 {
+      color: #3d3c38;
+      text-align: left;
+      font-size: 11px;
+      width: 100%;
+    }
+    .right {
+      display: flex;
+      align-content: center;
+      align-items: center;
+      justify-content: flex-end;
+      padding-right: 16px;
+      .right-button1 {
+        width: 58px;
+        height: 28px;
+        background: #fffffb;
+        opacity: 1;
+
+        font-size: 12px;
+        color: #3d3c38;
+        line-height: 28px;
+        text-align: center;
+        margin-left: 16px;
+      }
+      .button1_disabled {
+        background-color: #f9faf5;
+        color: #979893;
+      }
+      .right-button2 {
+        width: 58px;
+        height: 28px;
+        background: #fffffb;
+        opacity: 1;
+
+        font-size: 12px;
+
+        line-height: 28px;
+        text-align: center;
+        margin-left: 16px;
+      }
+    }
+  }
+}
+.container1 {
+  display: flex;
+  width: 119px;
+  flex-direction: column;
+}
+.coll {
+  position: relative;
+  display: flex;
+  margin-top: 9px;
+
+  .coll-header1 {
+    width: 231px;
+    height: 21px;
+    border-spacing: 0;
+    border-right: 1px solid #b8c0cc;
+    div {
+      box-sizing: border-box;
+    }
+    .coll-header1-row {
+      width: 21px;
+      height: 21px;
+      line-height: 21px;
+      background: #498ef5;
+      border-right: 1px solid #b8c0cc;
+      font-size: 10px;
+      white-space: nowrap;
+    }
+    // .coll-header1-row_border{
+    //   border-bottom: 1px solid #fff;
+    // }
+  }
+  .coll-header2 {
+    // position: absolute;
+    // top: 0px;
+    display: flex;
+    flex-direction: column;
+    flex-wrap: nowrap;
+    border-spacing: 0;
+    height: 237px;
+    width: 21px;
+
+    .coll-header2-col {
+      width: 23px;
+      height: 25px;
+      border-top: 1px solid #b8c0cc;
+      font-size: 10px;
+      line-height: 21px;
+      background: #498ef5;
+      white-space: nowrap;
+    }
+  }
+
+  .coll-table-topic {
+    background: #f2f3f5;
+    border-spacing: 0;
+    border-left: 1px solid #d2d1cf;
+    border-top: 1px solid #d2d1cf;
+    td {
+      border-bottom: 1px solid #d2d1cf;
+      border-right: 1px solid #d2d1cf;
+
+      div {
+        font-size: 8px;
+        width: 15px;
+        height: 17px;
+        line-height: 17px;
+      }
+      // font-size: 12px;
+      .coll-table-topic-item {
+        border: none;
+
+        white-space: nowrap;
+        div {
+          white-space: nowrap;
+        }
+      }
+    }
+    .collselected {
+      background: #498ef5;
+      color: #fff;
+    }
+  }
+}
+.answer {
+  width: 410px;
+  font-size: 13px;
+  text-align: left;
+  height: 41px;
+  line-height: 41px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  align-content: center;
+  flex-wrap: wrap;
+  padding-left: 18px;
+  padding-right: 13px;
+  border-left: 1px solid #b8c0cc;
+  .answer-tip {
+    color: #498ef5;
+    font-weight: 700;
+  }
+  .answer-list {
+    display: flex;
+    justify-content: space-between;
+    width: auto;
+    height: 24px;
+    .answer_selected {
+      color: #fff;
+      background: #498ef5;
+    }
+    .answer-list-item {
+      width: 24px;
+      height: 24px;
+      line-height: 24px;
+      border-radius: 2px;
+      font-size: 13px;
+      font-weight: 600;
+      border: 1px solid #b8c0cc;
+      border-radius: 6px;
+      text-align: center;
+      margin-left: 6px;
+    }
+    .answer-list-item_select {
+      color: #fff;
+      background: #498ef5;
+    }
+  }
+}
+.answer-img {
+  height: 74px;
+}
+.topic {
+  border: 1px solid #b8c0cc;
+  width: 410px;
+  height: 178px;
+  position: relative;
+  border-top: none;
+  border-right: none;
+  border-bottom: none;
+  padding-top: 6px;
+
+  .topic-issue {
+    font-size: 14px;
+    padding-left: 10px;
+    font-weight: normal;
+    text-align: left;
+    font-weight: 700;
+  }
+  .topic-header {
+    display: none;
+    color: #498ef5;
+    background: #f6f2f1;
+    top: 0;
+    left: 30px;
+    text-align: left;
+    padding-left: 2px;
+    padding-right: 2px;
+    white-space: nowrap;
+    font-size: 10px;
+  }
+  .topic-select {
+    display: flex;
+    width: 100%;
+    height: 37px;
+    padding-left: 10px;
+    padding-right: 10px;
+    font-size: 14px;
+    flex-wrap: wrap;
+  }
+  .topic-select-item {
+    width: 100%;
+    font-size: 14px;
+    text-align: left;
+    font-weight: 700;
+  }
+}
+.student {
+  border-bottom: 1px solid #b8c0cc;
+  width: 100%;
+  height: 175px;
+  font-size: 13px;
+  position: relative;
+  .student-header {
+    color: #498ef5;
+    background: #f6f2f1;
+    text-align: left;
+    padding-left: 2px;
+    padding-right: 2px;
+    white-space: nowrap;
+    font-weight: 600;
+    font-size: 10px;
+    text-align: center;
+    margin-top: 7px;
+  }
+  .driver {
+    width: 54px;
+    height: 54px;
+  }
+  .driverInfo {
+    font-size: 9px;
+    font-weight: 600;
+    padding-top: 10px;
+    text-align: left;
+    padding-left: 10px;
+    div {
+      margin-bottom: 6px;
+    }
+  }
+}
+.kaoti {
+  width: 100%;
+  position: relative;
+  height: 46px;
+  border-bottom: 1px solid #b8c0cc;
+  font-size: 13px;
+  font-weight: 600;
+  display: flex;
+  flex-wrap: wrap;
+  align-content: center;
+  align-items: center;
+  .kaoti-header {
+    color: #498ef5;
+    background: #f6f2f1;
+    width: 100%;
+    font-size: 10px;
+    text-align: center;
+    white-space: nowrap;
+    font-size: 12px;
+  }
+  .kaoti-headerText {
+    font-size: 8px;
+    font-weight: normal;
+    width: 100%;
+    text-align: center;
+    font-size: 11px;
+    color: #3d3c38;
+  }
+}
+.restTime {
+  width: 100%;
+  position: relative;
+  height: 42px;
+  font-size: 9px;
+  font-weight: 600;
+  display: flex;
+  align-content: center;
+  align-items: center;
+  flex-wrap: wrap;
+  // border-right: 1px solid #b8c0cc;
+  .restTime-header {
+    color: #498ef5;
+    font-size: 11px;
+    background: #f6f2f1;
+    width: 100%;
+    text-align: center;
+    padding-left: 8px;
+    padding-right: 8px;
+    white-space: nowrap;
+  }
+  .restTime-headerText {
+    font-size: 14px;
+    color: #3d3c38;
+    width: 100%;
+  }
+}
+.mt6 {
+  margin-top: 6px;
+}
+.mt9 {
+  margin-top: 9px;
+}
+.mt19 {
+  margin-top: 19px;
+}
+.font13 {
+  font-size: 13px;
+}
+</style>

+ 39 - 0
tsconfig.json

@@ -0,0 +1,39 @@
+{
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.d.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue"
+  ],
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+      "@/*": [
+        "src/*"
+      ],
+    },
+    "jsx": "preserve",
+    "target": "esnext",
+    "module": "esnext",
+    "sourceMap": true,
+    "outDir": "./dist",
+    "lib": [
+      "esnext",
+      "dom"
+    ],
+    "types": [
+      "vite/client",
+      "node"
+    ],
+    "strict": true,
+    "noUnusedLocals": true,
+    "noImplicitReturns": true,
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "plugins": [
+      {
+        "name": "@vuedx/typescript-plugin-vue"
+      }
+    ]
+  }
+}

+ 33 - 0
vite.config.ts

@@ -0,0 +1,33 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import path from 'path';
+import fs from "fs";
+const pathResolve = (pathStr: string) => {
+	return path.resolve(__dirname, pathStr);
+};
+
+module.exports = defineConfig({
+	plugins: [vue(), vueJsx()],
+	alias: {
+		'@': pathResolve('./src'),
+	},
+	server: {
+		// https: {
+		// 	key: fs.readFileSync("./cert/6353984_jpcj-h5.zzxcx.net.key"),
+		// 	cert: fs.readFileSync("./cert/6353984_jpcj-h5.zzxcx.net.pem"),
+		// },
+		proxy: {
+			"/dev-api": {
+				target: "https://jpcj-admin1.zzxcx.net/twzd-admin",
+				changeOrigin: true,
+				rewrite: (path) => path.replace(/^\/dev-api/, ""),
+			},
+			"/prod-api": {
+				target: "https://jpcj-admin.zzxcx.net/twzd-admin",
+				changeOrigin: true,
+				rewrite: (path) => path.replace(/^\/prod-api/, ""),
+			},
+		},
+	},
+});