|
@@ -1,20 +1,19 @@
|
|
|
<script setup lang="ts">
|
|
|
import moment from "moment";
|
|
|
import VueQrcode from "@chenfengyuan/vue-qrcode";
|
|
|
-import { onMounted, onUnmounted } from "vue";
|
|
|
+import { onMounted, onUnmounted, watch } from "vue";
|
|
|
import { useTimers } from "@/setups/useTimers";
|
|
|
-// import PulseLoader from "vue-spinner/src/PulseLoader.vue";
|
|
|
-// import {
|
|
|
-// openWS,
|
|
|
-// closeWsWithoutReconnect,
|
|
|
-// } from "@/features/OnlineExam/Examing/ws";
|
|
|
-
|
|
|
-// FIXME: 开启摄像头和websocket库
|
|
|
+import { Close, Checkmark } from "@vicons/ionicons5";
|
|
|
+import { closeMediaStream, getMediaStream } from "@/utils/camera";
|
|
|
+import { useWXSocket } from "./Examing/setups/useWXSocket";
|
|
|
+import { store } from "@/store/store";
|
|
|
+import { httpApp } from "@/plugins/axiosApp";
|
|
|
|
|
|
const emit = defineEmits<{ (e: "on-close"): void }>();
|
|
|
|
|
|
+const { addTimeout, addInterval } = useTimers();
|
|
|
+
|
|
|
const show = $ref(true);
|
|
|
-// const CLOCK_RATE_TIMEOUT = 10;
|
|
|
|
|
|
let current = $ref(1);
|
|
|
// @ts-expect-error chrome支持,但还有浏览器不支持,所以没进类型定义
|
|
@@ -59,18 +58,6 @@ const wechat = $ref({
|
|
|
examRecordDataId: null,
|
|
|
});
|
|
|
|
|
|
-// ...mapState([
|
|
|
-// "questionQrCode",
|
|
|
-// "questionQrCodeScanned",
|
|
|
-// "questionAnswerFileUrl",
|
|
|
-// ]),
|
|
|
-
|
|
|
-// timeCurrent() {
|
|
|
-// return moment(this.nowDate)
|
|
|
-// .utcOffset("+08:00")
|
|
|
-// .format("YYYY-MM-DD HH:mm:ssZZ");
|
|
|
-// },
|
|
|
-
|
|
|
const step1Status = $computed(() => {
|
|
|
return network.downlinkStatus && network.rrtStatus;
|
|
|
});
|
|
@@ -99,113 +86,158 @@ const step5Status = $computed(() => {
|
|
|
return wechat.qrScanned && wechat.uploadStatus;
|
|
|
});
|
|
|
|
|
|
-// watch(
|
|
|
-// () => questionQrCodeScanned,
|
|
|
-// () => {
|
|
|
-// this.wechat.qrScanned = true;
|
|
|
-// this.wechat.qrScannedResolved = true;
|
|
|
-// }
|
|
|
-// );
|
|
|
-// watch(
|
|
|
-// () => questionAnswerFileUrl,
|
|
|
-// (value) => {
|
|
|
-// const examRecordDataId = this.wechat.examRecordDataId;
|
|
|
-// for (const q of value) {
|
|
|
-// if (!q.saved) {
|
|
|
-// let acknowledgeStatus = "CONFIRMED";
|
|
|
-
|
|
|
-// this.$http
|
|
|
-// .post(
|
|
|
-// "/api/ecs_oe_student/examControl/saveUploadedFileAcknowledgeStatus",
|
|
|
-// {
|
|
|
-// examRecordDataId,
|
|
|
-// filePath: q.fileUrl,
|
|
|
-// order: q.order,
|
|
|
-// acknowledgeStatus,
|
|
|
-// }
|
|
|
-// )
|
|
|
-// .then(() => {
|
|
|
-// this.wechat.studentAnswer = q.fileUrl;
|
|
|
-// this.wechat.uploadResolved = true;
|
|
|
-// this.wechat.uploadStatus = true;
|
|
|
-// q.saved = true;
|
|
|
-// if (acknowledgeStatus === "CONFIRMED")
|
|
|
-// this.$Message.info({
|
|
|
-// content: "小程序作答已更新",
|
|
|
-// duration: 5,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// })
|
|
|
-// .catch(() => {
|
|
|
-// this.$Message.error({
|
|
|
-// content: "更新小程序答案失败!",
|
|
|
-// duration: 15,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// });
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-// );
|
|
|
-
|
|
|
+//#region 时钟处理
|
|
|
let nowDate: number = $ref(0);
|
|
|
-const { addInterval } = useTimers();
|
|
|
addInterval(() => (nowDate = Date.now()), 1000);
|
|
|
console.log(nowDate);
|
|
|
-
|
|
|
+const CLOCK_RATE_TIMEOUT = 10;
|
|
|
+let start: moment.Moment, end: moment.Moment;
|
|
|
+void fetch("/oe-web/", { method: "HEAD" }).then((e) => {
|
|
|
+ start = moment(e.headers.get("date"));
|
|
|
+});
|
|
|
+addTimeout(() => {
|
|
|
+ void fetch("/oe-web/", { method: "HEAD" }).then((e) => {
|
|
|
+ // 可能已经离开这个页面了
|
|
|
+ if (isUnmounted) return;
|
|
|
+ end = moment(e.headers.get("date"));
|
|
|
+ time.clockRateStateResolved = true;
|
|
|
+ // @ts-expect-error 不方便给clockRateDiff定义 number | null
|
|
|
+ time.clockRateDiff = end.diff(start, "seconds") - CLOCK_RATE_TIMEOUT;
|
|
|
+ time.clockRateStatus = end.diff(start, "seconds") < CLOCK_RATE_TIMEOUT + 2;
|
|
|
+ });
|
|
|
+}, CLOCK_RATE_TIMEOUT * 1000);
|
|
|
+
|
|
|
+let isUnmounted = false;
|
|
|
+onUnmounted(() => (isUnmounted = true));
|
|
|
+//#endregion 时钟处理
|
|
|
+
|
|
|
+//#region websocket
|
|
|
+// 初始化wxSocket的前提
|
|
|
+store.exam.WEIXIN_ANSWER_ENABLED = true;
|
|
|
+useWXSocket();
|
|
|
onMounted(async () => {
|
|
|
- // openWS({});
|
|
|
- // const fetchQR = async () => {
|
|
|
- // const examRecordDataId = this.$store.state.user.id;
|
|
|
- // const response = await this.$http.post(
|
|
|
- // "/api/ecs_oe_student/examControl/getQrCode",
|
|
|
- // {
|
|
|
- // examRecordDataId,
|
|
|
- // order: 1,
|
|
|
- // transferFileType: "AUDIO",
|
|
|
- // testEnv: true,
|
|
|
- // }
|
|
|
- // );
|
|
|
- // this.wechat.qrValue = response.data;
|
|
|
- // const trueExamRecordDataId = decodeURIComponent(response.data).match(
|
|
|
- // /&examRecordDataId=(\d+)/
|
|
|
- // )[1];
|
|
|
- // this.wechat.examRecordDataId = trueExamRecordDataId;
|
|
|
- // };
|
|
|
- // if (this.wechat.qrValue) {
|
|
|
- // this.getQRCodeTimeout = setTimeout(() => {
|
|
|
- // fetchQR();
|
|
|
- // }, 3000);
|
|
|
- // }
|
|
|
- // let start, end;
|
|
|
- // fetch("/oe-web/login", { Method: "HEAD" }).then((e) => {
|
|
|
- // start = moment(e.headers.get("date"));
|
|
|
- // });
|
|
|
- // this.checkClockRateTimeout = setTimeout(() => {
|
|
|
- // fetch("/oe-web/login", { Method: "HEAD" }).then((e) => {
|
|
|
- // // 可能已经离开这个页面了
|
|
|
- // if (!this.time) return;
|
|
|
- // end = moment(e.headers.get("date"));
|
|
|
- // 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);
|
|
|
- // await this.openCamera();
|
|
|
+ const examRecordDataId = store.user.id;
|
|
|
+ const response = await httpApp.post(
|
|
|
+ "/api/ecs_oe_student/examControl/getQrCode",
|
|
|
+ {
|
|
|
+ examRecordDataId,
|
|
|
+ order: 1,
|
|
|
+ transferFileType: "AUDIO",
|
|
|
+ testEnv: true,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ wechat.qrValue = response.data;
|
|
|
+ const trueExamRecordDataId = decodeURIComponent(wechat.qrValue).match(
|
|
|
+ /&examRecordDataId=(\d+)/
|
|
|
+ )![1];
|
|
|
+ // @ts-expect-error
|
|
|
+ wechat.examRecordDataId = trueExamRecordDataId;
|
|
|
});
|
|
|
+// websocket 会在unmounted时自动关闭
|
|
|
|
|
|
+watch(
|
|
|
+ () => store.exam.questionQrCodeScanned,
|
|
|
+ () => {
|
|
|
+ wechat.qrScanned = true;
|
|
|
+ wechat.qrScannedResolved = true;
|
|
|
+ }
|
|
|
+);
|
|
|
+watch(
|
|
|
+ () => store.exam.questionAnswerFileUrl,
|
|
|
+ (value) => {
|
|
|
+ const examRecordDataId = wechat.examRecordDataId;
|
|
|
+ for (const q of value) {
|
|
|
+ if (!q.saved) {
|
|
|
+ let acknowledgeStatus = "CONFIRMED";
|
|
|
+
|
|
|
+ httpApp
|
|
|
+ .post(
|
|
|
+ "/api/ecs_oe_student/examControl/saveUploadedFileAcknowledgeStatus",
|
|
|
+ {
|
|
|
+ examRecordDataId,
|
|
|
+ filePath: q.fileUrl,
|
|
|
+ order: q.order,
|
|
|
+ acknowledgeStatus,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ // @ts-expect-error
|
|
|
+ wechat.studentAnswer = q.fileUrl;
|
|
|
+ wechat.uploadResolved = true;
|
|
|
+ wechat.uploadStatus = true;
|
|
|
+ q.saved = true;
|
|
|
+ if (acknowledgeStatus === "CONFIRMED")
|
|
|
+ $message.info("小程序作答已更新");
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ $message.error("更新小程序答案失败!");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+//#endregion websocket
|
|
|
+
|
|
|
+//#region 摄像头处理
|
|
|
+onMounted(async () => {
|
|
|
+ await openCamera();
|
|
|
+});
|
|
|
+const video: HTMLVideoElement = $ref();
|
|
|
+async function openCamera() {
|
|
|
+ const stream = await getMediaStream();
|
|
|
+ video.srcObject = stream;
|
|
|
+ try {
|
|
|
+ await video.play();
|
|
|
+ camera.openCameraStatus = true;
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof Error) {
|
|
|
+ if (error.name == "AbortError") {
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ pgu: "AUTO",
|
|
|
+ act: "video.paly",
|
|
|
+ dtl: "AbortError and retry",
|
|
|
+ });
|
|
|
+ await video.play();
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ pgu: "AUTO",
|
|
|
+ act: "摄像头没有正常启用: AbortError 重试成功",
|
|
|
+ });
|
|
|
+ } else if (error.name == "NotSupportedError") {
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ act: "摄像头没有正常启用",
|
|
|
+ pgu: "AUTO",
|
|
|
+ ejn: JSON.stringify(error),
|
|
|
+ ext: {
|
|
|
+ errorName: error.name,
|
|
|
+ errorMessage: error.message,
|
|
|
+ errorStack: error.stack,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ $message.error("摄像头没有正常启用: " + error);
|
|
|
+ } else {
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ pgu: "AUTO",
|
|
|
+ act: "video.play",
|
|
|
+ dtl: "not an Error",
|
|
|
+ stk: error + "",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ throw error;
|
|
|
+ } finally {
|
|
|
+ camera.openCameraResolved = true;
|
|
|
+ }
|
|
|
+}
|
|
|
onUnmounted(() => {
|
|
|
- // clearTimeout(this.checkClockRateTimeout);
|
|
|
- // clearTimeout(this.getQRCodeTimeout);
|
|
|
- // if (this.$refs.video.srcObject) {
|
|
|
- // this.$refs.video.srcObject.getTracks().forEach(function (track) {
|
|
|
- // track.stop();
|
|
|
- // });
|
|
|
- // this.$refs.video.srcObject = null;
|
|
|
- // }
|
|
|
- // closeWsWithoutReconnect();
|
|
|
+ closeMediaStream();
|
|
|
});
|
|
|
+//#endregion 摄像头处理
|
|
|
|
|
|
function previous() {
|
|
|
if (current > 1) {
|
|
@@ -217,96 +249,19 @@ function next() {
|
|
|
current += 1;
|
|
|
}
|
|
|
if (current === 6) {
|
|
|
- // window._hmt.push([
|
|
|
- // "_trackEvent",
|
|
|
- // "环境检测",
|
|
|
- // `网络: ${this.step1Status}; 时间: ${this.step2Status}; 摄像头: ${this.step3Status}; 声音: ${this.step4Status}; 小程序: ${this.step5Status};`,
|
|
|
- // ]);
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ act: "环境检测",
|
|
|
+ ext: {
|
|
|
+ network: step1Status,
|
|
|
+ time: step2Status,
|
|
|
+ camera: step3Status,
|
|
|
+ sound: step4Status,
|
|
|
+ wechat: step5Status,
|
|
|
+ },
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-// async function openCamera() {
|
|
|
-// const video = this.$refs.video;
|
|
|
-// if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
|
-// try {
|
|
|
-// console.log("启动摄像头");
|
|
|
-// const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
-// video: {
|
|
|
-// facingMode: "user",
|
|
|
-// resizeMode: "crop-and-scale",
|
|
|
-// width: 400,
|
|
|
-// height: 300,
|
|
|
-// },
|
|
|
-// });
|
|
|
-// if (stream) {
|
|
|
-// video.srcObject = stream;
|
|
|
-// try {
|
|
|
-// await video.play();
|
|
|
-// this.camera.openCameraStatus = true;
|
|
|
-// } catch (error) {
|
|
|
-// console.log("摄像头没有正常启用", error);
|
|
|
-// this.$Message.error({
|
|
|
-// content: "摄像头没有正常启用: " + error,
|
|
|
-// duration: 15,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// window._hmt.push([
|
|
|
-// "_trackEvent",
|
|
|
-// "摄像头框-环境检测",
|
|
|
-// "摄像头状态",
|
|
|
-// "摄像头没有正常启用: " + error,
|
|
|
-// ]);
|
|
|
-// }
|
|
|
-// } else {
|
|
|
-// this.$Message.error({
|
|
|
-// content: "没有可用的视频流",
|
|
|
-// duration: 15,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// window._hmt.push([
|
|
|
-// "_trackEvent",
|
|
|
-// "摄像头框-环境检测",
|
|
|
-// "摄像头状态",
|
|
|
-// "没有可用的视频流",
|
|
|
-// ]);
|
|
|
-// }
|
|
|
-// } catch (error) {
|
|
|
-// console.log("无法启用摄像头", error);
|
|
|
-// let errMsg;
|
|
|
-// if (error.name || error.message) {
|
|
|
-// errMsg = `${error.name} ${error.message}`;
|
|
|
-// } else {
|
|
|
-// errMsg = error;
|
|
|
-// }
|
|
|
-// this.$Message.error({
|
|
|
-// content: "无法启用摄像头: " + errMsg,
|
|
|
-// duration: 15,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// window._hmt.push([
|
|
|
-// "_trackEvent",
|
|
|
-// "摄像头框-环境检测",
|
|
|
-// "摄像头状态",
|
|
|
-// "无法启用摄像头" + errMsg,
|
|
|
-// ]);
|
|
|
-// } finally {
|
|
|
-// this.camera.openCameraResolved = true;
|
|
|
-// }
|
|
|
-// } else {
|
|
|
-// this.$Message.error({
|
|
|
-// content: "没有找到可用的摄像头",
|
|
|
-// duration: 15,
|
|
|
-// closable: true,
|
|
|
-// });
|
|
|
-// window._hmt.push([
|
|
|
-// "_trackEvent",
|
|
|
-// "摄像头框-环境检测",
|
|
|
-// "摄像头状态",
|
|
|
-// "没有找到可用的摄像头",
|
|
|
-// ]);
|
|
|
-// }
|
|
|
-// this.camera.openCameraResolved = true;
|
|
|
-// }
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -342,13 +297,13 @@ function next() {
|
|
|
<td>{{ network.downlink }}Mb</td>
|
|
|
<td>
|
|
|
<div v-if="network.downlinkStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="下载速度不佳"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</td>
|
|
@@ -358,13 +313,13 @@ function next() {
|
|
|
<td>{{ network.rrt }}毫秒</td>
|
|
|
<td>
|
|
|
<div v-if="network.rrtStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="网络延迟较大"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</td>
|
|
@@ -384,27 +339,18 @@ function next() {
|
|
|
<td>状态</td>
|
|
|
</tr>
|
|
|
|
|
|
- <!-- <tr>
|
|
|
- <td>电脑当前时间</td>
|
|
|
- <td>{{ timeCurrent }} (北京时间)</td>
|
|
|
- <td></td>
|
|
|
- </tr> -->
|
|
|
- <!-- <tr>
|
|
|
- <td>电脑是否时间准确</td>
|
|
|
- <td>{{ timeDifference }}</td>
|
|
|
- </tr> -->
|
|
|
<tr>
|
|
|
<td>电脑时区</td>
|
|
|
<td>{{ time.currentTimeZone }}</td>
|
|
|
<td>
|
|
|
<div v-if="time.timeZoneStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="请将电脑设置为北京时区"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</td>
|
|
@@ -416,7 +362,7 @@ function next() {
|
|
|
{{ (time.clockRateDiff ?? 0) > 3 ? "时钟过慢" : "正常" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
@@ -424,17 +370,17 @@ function next() {
|
|
|
<n-icon
|
|
|
v-if="time.clockRateStatus"
|
|
|
class="pass-check"
|
|
|
- type="md-checkmark"
|
|
|
+ :component="Checkmark"
|
|
|
/>
|
|
|
<n-icon
|
|
|
v-else
|
|
|
class="fail-cross"
|
|
|
title="请更换电脑"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -446,13 +392,7 @@ function next() {
|
|
|
<div v-show="current === 3" key="3" class="section">
|
|
|
<div>
|
|
|
<div style="display: flex">
|
|
|
- <video
|
|
|
- id="video"
|
|
|
- ref="video"
|
|
|
- width="400"
|
|
|
- height="300"
|
|
|
- autoplay
|
|
|
- ></video>
|
|
|
+ <video id="video" ref="video" width="400" height="300" autoplay />
|
|
|
|
|
|
<div
|
|
|
v-if="camera.openCameraResolved && camera.openCameraStatus"
|
|
@@ -497,24 +437,24 @@ function next() {
|
|
|
{{ camera.openCameraStatus ? "正常" : "请检查摄像头" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
|
<div v-if="camera.openCameraResolved">
|
|
|
<div v-if="camera.openCameraStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="请检查摄像头"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -530,7 +470,7 @@ function next() {
|
|
|
{{ camera.identityStatus ? "正常" : "请检查摄像头" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
@@ -541,18 +481,18 @@ function next() {
|
|
|
"
|
|
|
>
|
|
|
<div v-if="camera.identityStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="请检查摄像头"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -624,24 +564,24 @@ function next() {
|
|
|
{{ sound.downloadStatus ? "正常" : "出错" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
|
<div v-if="sound.downloadResolved">
|
|
|
<div v-if="sound.downloadStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="下载出错"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -652,24 +592,24 @@ function next() {
|
|
|
{{ sound.playedStatus ? "正常" : "出错" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
|
<div v-if="sound.playedStatusResolved">
|
|
|
<div v-if="sound.playedStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="不能播放声音"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -699,7 +639,7 @@ function next() {
|
|
|
style="margin-top: 30px; font-size: 30px"
|
|
|
>
|
|
|
{{ wechat.studentAnswer ? "已上传" : "已扫描" }}
|
|
|
- <n-icon type="md-checkmark" />
|
|
|
+ <n-icon :component="Checkmark" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -763,24 +703,24 @@ function next() {
|
|
|
{{ wechat.qrScanned ? "正常" : "出错" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
|
<div v-if="wechat.qrScannedResolved">
|
|
|
<div v-if="wechat.qrScanned">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
<n-icon
|
|
|
class="fail-cross"
|
|
|
title="扫描出错"
|
|
|
- type="md-close"
|
|
|
+ :component="Close"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -792,20 +732,20 @@ function next() {
|
|
|
{{ wechat.uploadStatus ? "正常" : "出错" }}
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
<td>
|
|
|
<div v-if="wechat.uploadResolved">
|
|
|
<div v-if="wechat.uploadStatus">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -827,10 +767,10 @@ function next() {
|
|
|
<td>网速</td>
|
|
|
<td>
|
|
|
<div v-if="step1Status">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -839,14 +779,14 @@ function next() {
|
|
|
<td>
|
|
|
<div v-if="step2StatusResolved">
|
|
|
<div v-if="step2Status">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-spin size="medium" />
|
|
|
+ <n-spin size="small" />
|
|
|
</div>
|
|
|
</td>
|
|
|
</tr>
|
|
@@ -855,10 +795,10 @@ function next() {
|
|
|
<td>
|
|
|
<div v-if="step3StatusResolved">
|
|
|
<div v-if="step3Status">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else class="fail-cross">
|
|
@@ -871,10 +811,10 @@ function next() {
|
|
|
<td>
|
|
|
<div v-if="step4StatusResolved">
|
|
|
<div v-if="step4Status">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else class="fail-cross">
|
|
@@ -887,10 +827,10 @@ function next() {
|
|
|
<td>
|
|
|
<div v-if="step5StatusResolved">
|
|
|
<div v-if="step5Status">
|
|
|
- <n-icon class="pass-check" type="md-checkmark" />
|
|
|
+ <n-icon class="pass-check" :component="Checkmark" />
|
|
|
</div>
|
|
|
<div v-else>
|
|
|
- <n-icon class="fail-cross" type="md-close" />
|
|
|
+ <n-icon class="fail-cross" :component="Close" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else class="fail-cross">
|