QuestionBody.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <template>
  2. <div v-if="questionDetail" class="question-body" :key="examQuestion.questionId">
  3. <div v-html="questionDetail.text"></div>
  4. <!-- <div v-html="questionDetail.audio"></div> -->
  5. <div v-for="({src, name}, index) in questionDetail.audio" :key="name" class="audio-div">
  6. <div style="position: relative;" class="audio-div">
  7. <audio controls preload="auto" controlsList='nodownload' :key="src" :name="name" :src="src" @play="($event) => played(index, $event)" @ended="() => audioEnded(name)" @click="($event) => audioClicked(index, $event)"></audio>
  8. <span v-if="examQuestion.limitedPlayTimes">(剩余播放次数:{{getAudioPlayedTimes(name)}})</span><br />
  9. <div v-if="audioInPlay.has(name)" style="position: absolute;top: 0;right: 0;bottom: 0;left: 0;">
  10. </div>
  11. </div>
  12. </div>
  13. </div>
  14. <div v-else>
  15. 获取试题中...
  16. </div>
  17. </template>
  18. <script>
  19. import { createNamespacedHelpers } from "vuex";
  20. const { mapState, mapMutations } = createNamespacedHelpers("examingHomeModule");
  21. export default {
  22. name: "QuestionBody",
  23. data() {
  24. return {
  25. questionDetail: null,
  26. audioPlayTimes: null,
  27. audioInPlay: new Set()
  28. };
  29. },
  30. props: {
  31. questionBody: String,
  32. examQuestion: Object
  33. },
  34. mounted() {
  35. this.parseQuestion();
  36. },
  37. // updated() {
  38. // if (this.questionBody.includes("question-audio")) {
  39. // console.log(this.questionBody);
  40. // this.bindAudioPlay();
  41. // }
  42. // },
  43. methods: {
  44. ...mapMutations(["updateExamQuestion", "updateQuestionAudioPlayTimes"]),
  45. async parseQuestion() {
  46. let question = {};
  47. if (this.questionBody.includes("question-audio")) {
  48. // let audioArray = this.questionBody.match(/<a.*?question-audio.*?\/a>/g);
  49. // if (!this.examQuestion.audioPlayTimes) {
  50. // // 初始化音频播放次数
  51. // this.audioPlayTimes = [];
  52. // } else {
  53. // this.audioPlayTimes = JSON.parse(this.examQuestion.audioPlayTimes);
  54. // }
  55. question.text = this.questionBody.replace(
  56. /<a.*?question-audio.*?\/a>/g,
  57. ""
  58. );
  59. // 测试 v-html中含有directive是否生效。结论:不生效
  60. // question.text += "<span v-html='<text>abc</text>'>empty</span>";
  61. // audioArray = audioArray.map((v, i) => {
  62. // const remainTimes =
  63. // this.examQuestion.limitedPlayTimes -
  64. // this.examQuestion.audioPlayTimes[i];
  65. // if (remainTimes <= 0) {
  66. // v = v.replace("<a", "<a disabled"); // disabled不生效,仅仅起标明作用
  67. // v = v.replace(/url=/g, "");
  68. // }
  69. // return `${v}<span>(剩余播放次数:${remainTimes})</span><br/>`;
  70. // });
  71. // question.audio = audioArray
  72. // .join("")
  73. // .replace(/<a/g, "<audio controls controlsList='nodownload'")
  74. // .replace(/url=/g, "src=")
  75. // .replace(/a>/g, "audio>");
  76. // console.log(this.questionBody);
  77. // console.log(this.questionBody.match(/url="[^"]*"/g));
  78. // question.audio = this.questionBody
  79. // .match(/url="[^"]*"/g)
  80. // .map(v => v.replace("url="));
  81. // /question-audio.*?url="[^"]*"/.exec(this.questionBody)
  82. question.audio = [];
  83. let re = /name="([^"]*)".*?question-audio.*?url="([^"]*)"/g;
  84. let array1;
  85. while ((array1 = re.exec(this.questionBody)) !== null) {
  86. // console.log(`Found ${array1[0]}. Next starts at ${re.lastIndex}.`);
  87. // console.log(array1[1]);
  88. question.audio.push({ name: array1[1], src: array1[2] });
  89. // console.log(question.audio);
  90. // expected output: "Found foo. Next starts at 9."
  91. // expected output: "Found foo. Next starts at 19."
  92. }
  93. // setTimeout(this.bindAudioPlay.bind(this), 1000);
  94. } else {
  95. question.text = this.questionBody;
  96. question.audio = [];
  97. }
  98. this.questionDetail = question;
  99. },
  100. // bindAudioPlay() {
  101. // const audios = document.getElementsByTagName("audio");
  102. // if (audios.length === 0) {
  103. // // setTimeout(this.bindAudioPlay.bind(this), 1000);
  104. // return;
  105. // }
  106. // [...audios].forEach((audio, index) => {
  107. // console.log("bind audio: order:" + this.examQuestion.order);
  108. // const order = this.examQuestion.order;
  109. // const limitedPlayTimes = this.examQuestion.limitedPlayTimes;
  110. // let audioPlayTimes = this.examQuestion.audioPlayTimes;
  111. // audio.addEventListener("play", () => {
  112. // console.log("开始播放");
  113. // if (limitedPlayTimes - audioPlayTimes <= 0) {
  114. // console.log("无剩余播放次数");
  115. // }
  116. // audioPlayTimes[index] = audioPlayTimes[index] + 1;
  117. // this.updateExamQuestion({
  118. // order: order,
  119. // audioPlayTimes: audioPlayTimes
  120. // });
  121. // });
  122. // audio.addEventListener("ended", () => {
  123. // console.log("结束播放");
  124. // this.parseQuestion(); // 不会死循环,因为这个是音频播放触发的
  125. // });
  126. // });
  127. // },
  128. audioClicked(index, $event) {
  129. if ($event.target.paused === false) {
  130. // 正在播放中,不能暂停
  131. $event.preventDefault();
  132. }
  133. },
  134. audioEnded(name) {
  135. this.audioInPlay.delete(name);
  136. },
  137. played(index, $event) {
  138. if (!this.examQuestion.limitedPlayTimes) {
  139. // 如果没有设置音频播放次数
  140. return false;
  141. }
  142. const limitedPlayTimes = this.examQuestion.limitedPlayTimes;
  143. console.log("开始播放");
  144. const name = $event.target.attributes.name.value;
  145. // console.log(name);
  146. const theAudio = this.allAudioPlayTimes.find(a => a.name === name);
  147. const playedTimes = (theAudio || { times: 0 }).times;
  148. if (limitedPlayTimes - playedTimes <= 0) {
  149. console.log("无剩余播放次数");
  150. $event.target.pause();
  151. return;
  152. }
  153. this.audioInPlay.add(name);
  154. // var audio = new Audio($event.target.src);
  155. // audio.play();
  156. // if (theAudio) {
  157. // theAudio.times = playedTimes + 1;
  158. // } else {
  159. // this.audioPlayTimes.push({ name: name, times: 1 });
  160. // }
  161. if ($event.target.paused === false) {
  162. // 正在播放中
  163. this.updateQuestionAudioPlayTimes(name);
  164. }
  165. },
  166. getAudioPlayedTimes(name) {
  167. return Math.max(
  168. this.examQuestion.limitedPlayTimes -
  169. (this.allAudioPlayTimes.find(a => a.name === name) || { times: 0 })
  170. .times,
  171. 0
  172. );
  173. }
  174. },
  175. computed: {
  176. ...mapState(["allAudioPlayTimes"])
  177. },
  178. watch: {
  179. questionBody() {
  180. this.parseQuestion();
  181. },
  182. examQuestion() {
  183. this.parseQuestion();
  184. }
  185. }
  186. };
  187. </script>
  188. <style >
  189. .audio-div {
  190. display: flex;
  191. align-items: center;
  192. }
  193. /* .question-body audio {
  194. width: 180px;
  195. } */
  196. .question-body audio::-webkit-media-controls-timeline,
  197. .question-body audio::-webkit-media-controls-timeline-container {
  198. display: none;
  199. }
  200. </style>