RemainTime.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <template>
  2. <div class="remain-time">剩余时间 {{ remainTimeFormatted }}</div>
  3. </template>
  4. <script>
  5. import moment from "moment";
  6. import { createNamespacedHelpers } from "vuex";
  7. const { mapMutations } = createNamespacedHelpers("examingHomeModule");
  8. export default {
  9. name: "RemainTime",
  10. data() {
  11. return {
  12. remainTime: null
  13. };
  14. },
  15. async mounted() {
  16. this.heartbeatErrorNum = 0;
  17. this.getRemainTimeFromServer();
  18. this.heartbeatInterval = setInterval(() => {
  19. this.getRemainTimeFromServer();
  20. }, 60 * 1000);
  21. this.remainTimeInterval = setInterval(() => {
  22. if (this.remainTime > 0) {
  23. this.remainTime = this.remainTime - 1000;
  24. }
  25. }, 1000);
  26. },
  27. beforeDestroy() {
  28. this.clearIntervals();
  29. },
  30. methods: {
  31. ...mapMutations(["setShouldSubmitPaper", "updateRemainTime"]),
  32. async getRemainTimeFromServer() {
  33. if (this.$route.name !== "OnlineExamingHome") {
  34. // 非考试页,不继续发心跳。可能退出后再进,导致心跳多发一次。可能会话已过期。
  35. return;
  36. }
  37. try {
  38. const res = await this.$http.get(
  39. "/api/ecs_oe_student/examControl/examHeartbeat"
  40. );
  41. if (/\d+/.test(res.data)) {
  42. if (res.data <= 0) {
  43. this.setShouldSubmitPaper();
  44. }
  45. if (this.remainTime === null) {
  46. // 仅当剩余考试时间没有被初始化才使用服务器时间。否则使用本机时间。
  47. this.remainTime = res.data;
  48. }
  49. this.heartbeatErrorNum = 0;
  50. } else {
  51. this.$Message.error("服务器返回的心跳结果不是数字");
  52. }
  53. } catch (error) {
  54. if (
  55. error.response &&
  56. error.response.data &&
  57. error.response.data.desc &&
  58. error.response.data.desc.includes("会话已过期,请离开考试")
  59. ) {
  60. window._hmt.push([
  61. "_trackEvent",
  62. "正在考试页面",
  63. "心跳",
  64. "失败-会话过期"
  65. ]);
  66. this.serverLog(
  67. "debug/S-008002",
  68. `心跳失败-会话过期 => 考试剩余时间:${this.remainTime / 1000}`
  69. );
  70. } else {
  71. window._hmt.push(["_trackEvent", "正在考试页面", "心跳", "失败"]);
  72. this.serverLog(
  73. "debug/S-008003",
  74. `心跳失败 => 考试剩余时间:${this.remainTime / 1000} => ${error}`
  75. );
  76. }
  77. this.heartbeatErrorNum++;
  78. if (this.heartbeatErrorNum >= 10) {
  79. // 心跳异常10次则退出考试
  80. clearInterval(this.heartbeatInterval);
  81. clearInterval(this.remainTimeInterval);
  82. this.$Modal.error({
  83. title: "网络连接异常",
  84. content: "退出考试",
  85. onOk: () => {
  86. this.$router.push("/login/" + localStorage.getItem("domain"));
  87. }
  88. });
  89. return;
  90. }
  91. this.retryHeartbeatTimeout = setTimeout(
  92. () => this.getRemainTimeFromServer(),
  93. 10 * 1000
  94. );
  95. }
  96. },
  97. clearIntervals() {
  98. clearInterval(this.heartbeatInterval);
  99. clearInterval(this.remainTimeInterval);
  100. clearTimeout(this.retryHeartbeatTimeout);
  101. }
  102. },
  103. computed: {
  104. remainTimeFormatted: function() {
  105. return moment.utc(this.remainTime).format("HH:mm:ss");
  106. }
  107. },
  108. watch: {
  109. remainTime(val) {
  110. if (val !== null && val === 0) {
  111. this.setShouldSubmitPaper();
  112. }
  113. this.updateRemainTime(val);
  114. }
  115. }
  116. };
  117. </script>