소스 검색

美化 环境检测

Michael Wang 5 년 전
부모
커밋
60956421ec
4개의 변경된 파일377개의 추가작업 그리고 108개의 파일을 삭제
  1. 1 0
      package.json
  2. 367 107
      src/features/OnlineExam/CheckComputer.vue
  3. 4 1
      src/features/OnlineExam/OnlineExamList.vue
  4. 5 0
      yarn.lock

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "viewerjs": "^1.3.6",
     "vue": "^2.6.10",
     "vue-router": "^3.1.2",
+    "vue-spinner": "^1.0.3",
     "vue-splitpane": "^1.0.4",
     "vuedraggable": "^2.23.0",
     "vuex": "^3.1.1"

+ 367 - 107
src/features/OnlineExam/CheckComputer.vue

@@ -15,24 +15,41 @@
           <tbody class="list-row">
             <tr class="list-header qm-primary-strong-text">
               <td class="first-td">检查项</td>
-              <td>结果</td>
+              <td>值</td>
+              <td>状态</td>
             </tr>
 
             <tr>
-              <td>电脑当前时间</td>
-              <td>{{ timeCurrent }} (北京时间)</td>
-            </tr>
-            <!-- <tr>
-              <td>电脑是否时间准确</td>
-              <td>{{ timeDifference }}</td>
-            </tr> -->
-            <tr>
-              <td>电脑时区</td>
-              <td>{{ timeZone }}</td>
+              <td>电脑当前下载速度</td>
+              <td>{{ network.downlink }}Mb</td>
+              <td>
+                <div v-if="network.downlinkStatus">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon
+                    class="fail-cross"
+                    title="下载速度不佳"
+                    type="md-close"
+                  />
+                </div>
+              </td>
             </tr>
             <tr>
-              <td>电脑时钟频率</td>
-              <td>{{ clockRateResult }}</td>
+              <td>电脑当前网络延迟</td>
+              <td>{{ network.rrt }}毫秒</td>
+              <td>
+                <div v-if="network.rrtStatus">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon
+                    class="fail-cross"
+                    title="网络延迟较大"
+                    type="md-close"
+                  />
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -45,16 +62,63 @@
           <tbody class="list-row">
             <tr class="list-header qm-primary-strong-text">
               <td class="first-td">检查项</td>
-              <td>结果</td>
+              <td>值</td>
+              <td>状态</td>
             </tr>
 
+            <!-- <tr>
+              <td>电脑当前时间</td>
+              <td>{{ timeCurrent }} (北京时间)</td>
+              <td></td>
+            </tr> -->
+            <!-- <tr>
+              <td>电脑是否时间准确</td>
+              <td>{{ timeDifference }}</td>
+            </tr> -->
             <tr>
-              <td>电脑当前下载速度</td>
-              <td>{{ network.downlink }}</td>
+              <td>电脑时区</td>
+              <td>{{ time.currentTimeZone }}</td>
+              <td>
+                <div v-if="time.timeZoneStatus">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon
+                    class="fail-cross"
+                    title="请将电脑设置为北京时区"
+                    type="md-close"
+                  />
+                </div>
+              </td>
             </tr>
             <tr>
-              <td>电脑当前网络延迟</td>
-              <td>{{ network.rrt }}</td>
+              <td>电脑时钟频率</td>
+              <td>
+                <div v-if="time.clockRateStateResolved">
+                  {{ time.clockRateDiff > 3 ? "时钟过慢" : "正常" }}
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+              <td>
+                <div v-if="time.clockRateStateResolved">
+                  <Icon
+                    class="pass-check"
+                    v-if="time.clockRateStatus"
+                    type="md-checkmark"
+                  />
+                  <Icon
+                    class="fail-cross"
+                    v-else
+                    title="请更换电脑"
+                    type="md-close"
+                  />
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -63,16 +127,16 @@
 
     <div v-show="current === 2" class="section">
       <div>
-        <div style="display: flex;">
+        <div style="display: flex; justify-content: center;">
           <video
             id="video"
             ref="video"
-            :width="400"
-            :height="300"
+            width="400"
+            height="300"
             autoplay
           ></video>
 
-          <div style="margin-left: 50px; margin-top: 100px;">
+          <!-- <div style="margin-left: 50px; margin-top: 100px;">
             <Button type="warning" @click="camera = '没有摄像头'">
               摄像头没有启用
             </Button>
@@ -80,7 +144,7 @@
             <Button type="primary" @click="camera = 'OK'">
               摄像头正常工作
             </Button>
-          </div>
+          </div> -->
         </div>
       </div>
 
@@ -89,12 +153,25 @@
           <tbody class="list-row">
             <tr class="list-header qm-primary-strong-text">
               <td class="first-td">检查项</td>
-              <td>结果</td>
+              <td>值</td>
+              <td>状态</td>
             </tr>
 
             <tr>
-              <td>摄像头没有启用</td>
-              <td>{{ camera }}</td>
+              <td>摄像头正常启用</td>
+              <td>{{ camera ? "正常" : "请检查摄像头" }}</td>
+              <td>
+                <div v-if="camera">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon
+                    class="fail-cross"
+                    title="请检查摄像头"
+                    type="md-close"
+                  />
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -108,18 +185,35 @@
             src="https://ecs-static.qmth.com.cn/check-audio.mp3"
             controls
             nodownload
+            @loadeddata="
+              sound.downloadResolved = true;
+              sound.downloadStatus = true;
+            "
+            @error="
+              sound.downloadResolved = true;
+              sound.downloadStatus = false;
+            "
           />
 
           <div style="margin-left: 30px; display: flex; ">
             <Button
               type="warning"
-              @click="sound = '没有声音'"
+              @click="
+                sound.playedStatusResolved = true;
+                sound.playedStatus = false;
+              "
               title="或者听不到声音"
             >
               不能播放声音
             </Button>
             <div style="width: 30px; height: 30px;"></div>
-            <Button type="primary" @click="sound = 'OK'">
+            <Button
+              type="primary"
+              @click="
+                sound.playedStatusResolved = true;
+                sound.playedStatus = true;
+              "
+            >
               能够播放声音
             </Button>
           </div>
@@ -131,12 +225,61 @@
           <tbody class="list-row">
             <tr class="list-header qm-primary-strong-text">
               <td class="first-td">检查项</td>
-              <td>结果</td>
+              <td>值</td>
+              <td>状态</td>
             </tr>
 
             <tr>
-              <td>声音</td>
-              <td>{{ sound }}</td>
+              <td>文件下载</td>
+              <td>
+                <div v-if="sound.downloadResolved">
+                  {{ sound.downloadStatus ? "正常" : "出错" }}
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+              <td>
+                <div v-if="sound.downloadResolved">
+                  <div v-if="sound.downloadStatus">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" title="下载出错" type="md-close" />
+                  </div>
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <td>声音播放</td>
+              <td>
+                <div v-if="sound.playedStatusResolved">
+                  {{ sound.playedStatus ? "正常" : "出错" }}
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+              <td>
+                <div v-if="sound.playedStatusResolved">
+                  <div v-if="sound.playedStatus">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon
+                      class="fail-cross"
+                      title="不能播放声音"
+                      type="md-close"
+                    />
+                  </div>
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -154,7 +297,7 @@
                 style="margin-left: -10px;"
               ></qrcode>
               <div style="margin-top: 10px;">
-                <div>
+                <div style="font-size: 30px;">
                   请使用<span style="font-weight: 900; color: #1E90FF;"
                     >微信</span
                   >扫描二维码后,在微信小程序上录音,并上传文件。
@@ -176,7 +319,7 @@
           class="audio-answer audio-answer-line-height"
           style="margin-top: 20px; text-align: left;"
         >
-          <span class="audio-answer-line-height">答案:</span>
+          <span class="audio-answer-line-height">上传文件:</span>
           <audio
             class="audio-answer-line-height"
             v-if="this.wechat.studentAnswer"
@@ -190,14 +333,24 @@
         <div style=" margin-top: 30px; display: flex; margin-bottom: 30px;">
           <Button
             type="warning"
-            @click="wechat.upload = '不能正确扫描'"
-            title="或者上传不成功"
+            @click="
+              wechat.qrScannedResolved = true;
+              wechat.qrScanned = false;
+            "
+            title="扫码不成功"
           >
-            不能正确扫描,或者上传不成功
+            不能正确扫描二维码
           </Button>
           <div style="width: 30px; height: 30px;"></div>
-          <Button type="primary" @click="wechat.upload = 'OK'">
-            能够扫描,并上传成功
+          <Button
+            type="warning"
+            @click="
+              wechat.uploadResolved = true;
+              wechat.uploadStatus = false;
+            "
+            title="上传不成功"
+          >
+            上传不成功
           </Button>
         </div>
       </div>
@@ -207,12 +360,58 @@
           <tbody class="list-row">
             <tr class="list-header qm-primary-strong-text">
               <td class="first-td">检查项</td>
-              <td>结果</td>
+              <td>值</td>
+              <td>状态</td>
             </tr>
 
             <tr>
-              <td>扫描二维码并上传录音</td>
-              <td>{{ wechat.upload }}</td>
+              <td>扫描二维码</td>
+              <td>
+                <div v-if="wechat.qrScannedResolved">
+                  {{ wechat.qrScannedq ? "正常" : "出错" }}
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+              <td>
+                <div v-if="wechat.qrScannedResolved">
+                  <div v-if="wechat.qrScannedq">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" title="下载出错" type="md-close" />
+                  </div>
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+            </tr>
+
+            <tr>
+              <td>上传录音</td>
+              <td>
+                <div v-if="wechat.uploadResolved">
+                  {{ wechat.uploadStatus ? "正常" : "出错" }}
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
+              <td>
+                <div v-if="wechat.uploadResolved">
+                  <div v-if="wechat.uploadStatus">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" type="md-close" />
+                  </div>
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -229,24 +428,74 @@
             </tr>
 
             <tr>
-              <td>时钟</td>
-              <td>{{ step1Status }}</td>
+              <td>网速</td>
+              <td>
+                <div v-if="step1Status">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon class="fail-cross" type="md-close" />
+                </div>
+              </td>
             </tr>
             <tr>
-              <td>网速</td>
-              <td>{{ step2Status }}</td>
+              <td>时钟</td>
+              <td>
+                <div v-if="step2StatusResolved">
+                  <div v-if="step2Status">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" type="md-close" />
+                  </div>
+                </div>
+                <div v-else>
+                  <PulseLoader />
+                </div>
+              </td>
             </tr>
             <tr>
               <td>摄像头</td>
-              <td>{{ step3Status }}</td>
+              <td>
+                <div v-if="step3Status">
+                  <Icon class="pass-check" type="md-checkmark" />
+                </div>
+                <div v-else>
+                  <Icon class="fail-cross" type="md-close" />
+                </div>
+              </td>
             </tr>
             <tr>
               <td>声音</td>
-              <td>{{ step4Status }}</td>
+              <td>
+                <div v-if="step4StatusResolved">
+                  <div v-if="step4Status">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" type="md-close" />
+                  </div>
+                </div>
+                <div v-else class="fail-cross">
+                  请在“声音”步骤进行人工确认!
+                </div>
+              </td>
             </tr>
             <tr>
               <td>微信小程序</td>
-              <td>{{ step5Status }}</td>
+              <td>
+                <div v-if="step5StatusResolved">
+                  <div v-if="step5Status">
+                    <Icon class="pass-check" type="md-checkmark" />
+                  </div>
+                  <div v-else>
+                    <Icon class="fail-cross" type="md-close" />
+                  </div>
+                </div>
+                <div v-else class="fail-cross">
+                  请在“微信小程序”步骤进行人工确认!
+                </div>
+              </td>
             </tr>
           </tbody>
         </table>
@@ -275,7 +524,9 @@
 
 <script>
 import moment from "moment";
+// import "moment-duration-format";
 import VueQrcode from "@chenfengyuan/vue-qrcode";
+import PulseLoader from "vue-spinner/src/PulseLoader.vue";
 import {
   openWS,
   closeWsWithoutReconnect,
@@ -286,35 +537,43 @@ const { mapState } = createNamespacedHelpers("examingHomeModule");
 
 const CLOCK_RATE_TIMEOUT = 10;
 
+// console.log(
+//   moment.duration(-new Date().getTimezoneOffset(), "minutes").format("hh:mm")
+// );
 export default {
   name: "check-computer",
   data() {
     return {
       current: 0,
       // status: ["process", "wait", "wait", "wait", "wait"],
-      nowDate: Date.now(),
-      timeDifference: 0,
-      timeZone:
-        new Date().getTimezoneOffset() / 60 === -8
-          ? "OK"
-          : "请将电脑设置为北京时区",
-      clockRateResult: "检测中",
       network: {
-        downlink:
-          navigator.connection.downlink > 0.5
-            ? "OK"
-            : `下载速度不佳: ${navigator.connection.downlink}Mb`,
-        rrt:
-          navigator.connection.rtt < 1000
-            ? "OK"
-            : `网络延迟较大${navigator.connection.rtt}`,
+        downlink: navigator.connection.downlink,
+        downlinkStatus: navigator.connection.downlink > 0.5,
+        rrt: navigator.connection.rtt,
+        rrtStatus: navigator.connection.rtt < 1000,
+      },
+      time: {
+        // nowDate: Date.now(),
+        // timeDifference: 0,
+        currentTimeZone: moment().format("Z"),
+        timeZoneStatus: new Date().getTimezoneOffset() / 60 === -8,
+        clockRateDiff: null,
+        clockRateStateResolved: false,
+        clockRateStatus: false,
+      },
+      camera: false,
+      sound: {
+        downloadResolved: false,
+        downloadStatus: false,
+        playedStatusResolved: false,
+        playedStatus: false,
       },
-      camera: "待确认",
-      sound: "待确认",
       wechat: {
         qrValue: " ",
-        upload: "待确认",
+        qrScannedResolved: false,
         qrScanned: false,
+        uploadResolved: false,
+        uploadStatus: false,
         studentAnswer: null,
         examRecordDataId: null,
       },
@@ -333,24 +592,25 @@ export default {
   },
   async mounted() {
     let start, end;
-    const networkStart = performance.now();
+    // const networkStart = performance.now();
     fetch("/oe/login", { Method: "HEAD" }).then(e => {
       start = moment(e.headers.get("date"));
-      const networkEnd = performance.now();
+      // const networkEnd = performance.now();
 
-      this.timeDifference =
-        Math.abs(moment().diff(start) - (networkEnd - networkStart) / 2) <
-        60 * 1000
-          ? "OK"
-          : "与服务器时间差异超过60秒";
+      // this.timeDifference =
+      //   Math.abs(moment().diff(start) - (networkEnd - networkStart) / 2) <
+      //   60 * 1000
+      //     ? "OK"
+      //     : "与服务器时间差异超过60秒";
     });
     this.checkClockRateTimeout = setTimeout(() => {
       fetch("/oe/login", { Method: "HEAD" }).then(e => {
         end = moment(e.headers.get("date"));
-        this.clockRateResult =
-          end.diff(start, "seconds") < CLOCK_RATE_TIMEOUT + 2
-            ? "OK"
-            : "时钟过慢";
+        this.time.clockRateStateResolved = true;
+        this.time.clockRateDiff =
+          end.diff(start, "seconds") - CLOCK_RATE_TIMEOUT;
+        this.time.clockRateStatus =
+          end.diff(start, "seconds") < CLOCK_RATE_TIMEOUT + 2;
       });
     }, CLOCK_RATE_TIMEOUT * 1000);
 
@@ -393,6 +653,7 @@ export default {
             video.srcObject = stream;
             try {
               await video.play();
+              this.camera = true;
             } catch (error) {
               this.$Message.error({
                 content: "摄像头没有正常启用",
@@ -408,7 +669,7 @@ export default {
             });
             window._hmt.push([
               "_trackEvent",
-              "摄像头框",
+              "摄像头框-环境检测",
               "摄像头状态",
               "没有可用的视频流",
             ]);
@@ -421,7 +682,7 @@ export default {
           });
           window._hmt.push([
             "_trackEvent",
-            "摄像头框",
+            "摄像头框-环境检测",
             "摄像头状态",
             "无法启用摄像头",
           ]);
@@ -434,7 +695,7 @@ export default {
         });
         window._hmt.push([
           "_trackEvent",
-          "摄像头框",
+          "摄像头框-环境检测",
           "摄像头状态",
           "没有找到可用的摄像头",
         ]);
@@ -453,42 +714,28 @@ export default {
         .format("YYYY-MM-DD HH:mm:ssZZ");
     },
     step1Status() {
-      // if (this.current === 0) {
-      //   return "process";
-      // }
-      // return this.timeDifference === "OK" &&
-      return this.timeZone === "OK" && this.clockRateResult === "OK"
-        ? "OK"
-        : "错误";
+      return this.network.downlinkStatus && this.network.rrtStatus;
+    },
+    step2StatusResolved() {
+      return this.time.clockRateStateResolved;
     },
     step2Status() {
-      // if (this.current <= 1) {
-      //   return "process";
-      // }
-      return this.network.downlink === "OK" && this.network.rrt === "OK"
-        ? "OK"
-        : "错误";
+      return this.time.timeZoneStatus && this.time.clockRateStatus;
     },
     step3Status() {
-      // if (this.current <= 2) {
-      //   return "process";
-      // }
-      // return this.camera === "OK" ? "OK" : "错误";
       return this.camera;
     },
+    step4StatusResolved() {
+      return this.sound.downloadResolved && this.sound.playedStatusResolved;
+    },
     step4Status() {
-      // if (this.current <= 3) {
-      //   return "process";
-      // }
-      // return this.sound === "OK" ? "OK" : "错误";
-      return this.sound;
+      return this.sound.downloadStatus && this.sound.playedStatus;
+    },
+    step5StatusResolved() {
+      return this.wechat.qrScannedResolved && this.wechat.uploadResolved;
     },
     step5Status() {
-      // if (this.current <= 4) {
-      //   return "process";
-      // }
-      // return this.wechat.upload === "OK" ? "OK" : "错误";
-      return this.wechat.upload;
+      return this.wechat.qrScanned && this.wechat.uploadStatus;
     },
   },
   watch: {
@@ -504,6 +751,7 @@ export default {
       // console.log(this.examQuestion.studentAnswer);
       // console.log("watch", value);
       this.wechat.qrScanned = true;
+      this.wechat.qrScannedResolved = true;
     },
     questionAnswerFileUrl(value) {
       // console.log(this.examQuestion.studentAnswer);
@@ -525,6 +773,7 @@ export default {
             )
             .then(() => {
               this.wechat.studentAnswer = q.fileUrl;
+              this.wechat.uploadResolved = true;
               q.saved = true;
               if (acknowledgeStatus === "CONFIRMED")
                 this.$Message.info({
@@ -546,6 +795,7 @@ export default {
   },
   components: {
     qrcode: VueQrcode,
+    PulseLoader,
   },
 };
 </script>
@@ -574,4 +824,14 @@ export default {
 .list .first-td {
   width: 50%;
 }
+
+.pass-check {
+  font-size: 20px;
+  color: green;
+}
+
+.fail-cross {
+  font-size: 20px;
+  color: red;
+}
 </style>

+ 4 - 1
src/features/OnlineExam/OnlineExamList.vue

@@ -77,7 +77,10 @@
       :closable="false"
       :mask-closable="false"
     >
-      <CheckComputer @on-close="resumeEnterExam" />
+      <CheckComputer
+        v-if="shouldShowCheckEnvModal"
+        @on-close="resumeEnterExam"
+      />
     </Modal>
   </div>
 </template>

+ 5 - 0
yarn.lock

@@ -10699,6 +10699,11 @@ vue-router@^3.1.2:
   resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.2.tgz#2e0904703545dabdd42b2b7a2e617f02f99a1969"
   integrity sha512-WssQEHSEvIS1/CI4CO2T8LJdoK4Q9Ngox28K7FDNMTfzNTk2WS5D0dDlqYCaPG+AG4Z8wJkn1KrBc7AhspZJUQ==
 
+vue-spinner@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/vue-spinner/-/vue-spinner-1.0.3.tgz#eecd3f79f63a815b690e9d54b199ab65be695499"
+  integrity sha512-Phh6QC/Bh80ZE/0SH9ADw4irbRGwFK6VZsTksPHpPm16xga2OXwNC2nI7SEPnndUZP+Lgu1wu+pbH6i6+5siBQ==
+
 vue-splitpane@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/vue-splitpane/-/vue-splitpane-1.0.4.tgz#cdf744a8f31bedcb086e9545a9453565d360bd02"