index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <template>
  2. <div style="position: relative;">
  3. <el-dialog title="交互视频" :visible.sync="dialogVisibleVideo" :append-to-body="true" width="95%"
  4. :before-close="handleClose" class="dialog_diy">
  5. <div v-if="dialogVisibleVideo">
  6. <div class="videoBox">
  7. <video-player class="video-player vjs-custom-skin" ref="videoPlayer" :playsinline="true"
  8. :options="playerO" v-if="this.json.video" @timeupdate="onPlayerTimeupdate($event)"></video-player>
  9. <div class="chooseNodeBox">
  10. <div class="chooseNodeItem" v-for="(item,index) in json.setting" @click.stop="chooseNode(item)">
  11. <!-- <div v-if="userChooseAnswer.find(i=>i.testJson[0].teststitle==item.tool.toolJson.testJson[0].teststitle)" class="el-icon-circle-check"></div> -->
  12. <div v-if="userChooseAnswer[index]" class="el-icon-circle-check"></div>
  13. <div v-else>{{ index+1 }}</div>
  14. </div>
  15. </div>
  16. </div>
  17. <div class="accuracy" v-if="accuracyList.length>0">
  18. <span>准确率:</span>
  19. <div class="accuracyItem" v-for="(item,index) in accuracyList">
  20. <div class="accuracyTitle">第 {{ index+1 }} 题:</div>
  21. <div class="accuracyValue">正确率{{item}}%</div>
  22. </div>
  23. </div>
  24. </div>
  25. <span slot="footer" class="dialog-footer">
  26. <el-button @click="close">关 闭</el-button>
  27. <el-button @click.stop="submitBtn" type="primary">确认</el-button>
  28. </span>
  29. </el-dialog>
  30. <choiceD
  31. ref="choiceD"
  32. :dialogVisibleChoice.sync="dialogVisibleChoice"
  33. :json="toolJson"
  34. :userChooseAnswer:="userChooseAnswer"
  35. @play="gotoPlay"
  36. :userid="userid"
  37. :id="id"
  38. :courseType="courseType"
  39. :taskCount="taskCount"
  40. :toolindex="toolindex"
  41. :videoTime="stopTime"
  42. :videoType="videoType"
  43. @success="choiceAnswer"
  44. ></choiceD>
  45. </div>
  46. </template>
  47. <script>
  48. import choiceD from '../Choice/index.vue'
  49. export default {
  50. emits:['success'],
  51. props: {
  52. dialogVisibleVideo: {
  53. type: Boolean,
  54. default: false
  55. },
  56. videoJson: {
  57. type: Object
  58. },
  59. userid: {
  60. type: String,
  61. },
  62. id: {
  63. type: String,
  64. },
  65. courseType: {
  66. type: String || Number,
  67. },
  68. taskCount: {
  69. type: Number,
  70. },
  71. toolindex: {
  72. type: Number,
  73. },
  74. videoType:{
  75. type:Number,
  76. default:0
  77. }
  78. },
  79. components: {
  80. choiceD,
  81. },
  82. data() {
  83. return {
  84. json: {},
  85. userChooseAnswer:[],
  86. accuracyList:[],
  87. playerOptions: {
  88. playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
  89. autoplay: false, //如果true,浏览器准备好时开始回放。
  90. muted: false, // 默认情况下将会消除任何音频。
  91. loop: false, // 导致视频一结束就重新开始。
  92. preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
  93. language: "zh-CN",
  94. aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
  95. fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
  96. sources: [
  97. {
  98. type: "video/mp4", //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目 || "video/ogg"|| "video/webm"
  99. src: "", //url地址require("../../assets/media/aaa.mp4")
  100. },
  101. ],
  102. // poster: require("../../assets/tu31.png"), //你的封面地址
  103. // poster: dataRes.imgUrl, //你的封面地址
  104. notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
  105. controlBar: {
  106. timeDivider: true, //当前时间和持续时间的分隔符
  107. durationDisplay: true, //显示持续时间
  108. remainingTimeDisplay: false, //是否显示剩余时间功能
  109. fullscreenToggle: false, //全屏按钮
  110. },
  111. },
  112. playerO: {},
  113. videoTime: 0,
  114. dialogVisibleChoice: false,
  115. stopTime: 0,
  116. toolJson: {}
  117. }
  118. },
  119. watch: {
  120. dialogVisibleVideo(newValue, oldValue) {
  121. this.json = JSON.parse(JSON.stringify(this.videoJson));
  122. if (this.json.video) {
  123. this.playerO = JSON.parse(JSON.stringify(this.playerOptions));
  124. this.playerO.sources[0].src = this.json.video
  125. this.videoType==1?this.userChooseAnswer = this.json.setting.map(i=>i.tool.toolJson):[];
  126. this.getAccuracyList();
  127. this.$nextTick(() => {
  128. setTimeout(() => {
  129. this.videoTime = Math.round(this.$refs['videoPlayer'].player.cache_.duration)
  130. }, 500)
  131. })
  132. }
  133. if(!newValue)this.json = [];
  134. }
  135. },
  136. methods: {
  137. handleClose(done) {
  138. this.close()
  139. done()
  140. },
  141. close() {
  142. this.$emit("update:dialogVisibleVideo", false)
  143. },
  144. onPlayerTimeupdate(player) {
  145. let gklog = player.cache_.currentTime;//当前播放的秒数
  146. let gklog2 = parseInt(gklog)
  147. for (var i = 0; i < this.json.setting.length; i++) {
  148. if (gklog2 == this.json.setting[i].time) {
  149. this.stopTime = this.json.setting[i].time
  150. player.pause()
  151. if (this.json.setting[i].tool.tool == 45) {
  152. this.dialogVisibleChoice = true
  153. this.toolJson = this.json.setting[i].tool.toolJson
  154. let answerIndex = this.json.setting.findIndex(i=>JSON.stringify(i.tool.toolJson)==JSON.stringify(this.toolJson))
  155. if(answerIndex!=-1 && this.userChooseAnswer[answerIndex]){
  156. this.$refs['choiceD'].setUserAnswer(this.userChooseAnswer[answerIndex].testJson.map(i=>i.answer2))
  157. }
  158. }
  159. }
  160. }
  161. console.log(" onPlayerTimeupdate!", gklog);
  162. console.log(" onPlayerTimeupdate!", gklog2);
  163. },
  164. gotoPlay() {
  165. this.$refs['videoPlayer'].player.currentTime(this.stopTime + 1)
  166. this.$refs['videoPlayer'].player.play()
  167. },
  168. // 点击了节点
  169. chooseNode(setting){
  170. this.$refs['videoPlayer'].player.currentTime(setting.time)
  171. this.stopTime = setting.time;
  172. this.$refs['videoPlayer'].player.pause()
  173. if (setting.tool.tool == 45) {
  174. this.dialogVisibleChoice = true
  175. this.toolJson = setting.tool.toolJson
  176. let answerIndex = this.json.setting.findIndex(i=>JSON.stringify(i.tool.toolJson)==JSON.stringify(this.toolJson))
  177. if(answerIndex!=-1 && this.userChooseAnswer[answerIndex]){
  178. this.$refs['choiceD'].setUserAnswer(this.userChooseAnswer[answerIndex].testJson.map(i=>i.answer2))
  179. }
  180. }
  181. },
  182. // 选择答案并且确定
  183. choiceAnswer(data){
  184. let answer = data.answer;
  185. let testJson = data.json;
  186. let changeIndex = this.json.setting.findIndex(i=>JSON.stringify(i.tool.toolJson)==JSON.stringify(testJson))
  187. if(changeIndex!=-1){
  188. testJson.testJson.forEach((i,index)=>{
  189. i.answer2 = answer[index];
  190. })
  191. this.userChooseAnswer[changeIndex] = testJson;
  192. }
  193. this.getAccuracyList();
  194. },
  195. getAccuracyList(){
  196. if(this.userChooseAnswer.length<0)return;
  197. let score = [];
  198. this.json.setting.forEach((item,index)=>{
  199. if(this.userChooseAnswer[index]){
  200. let count = 0;
  201. let type = item.tool.toolJson.testJson.map(i=>i.type)
  202. let userAnswer = this.userChooseAnswer[index].testJson.map(i=>i.answer2);
  203. let rightAnswer = this.userChooseAnswer[index].testJson.map(i=>i.answer);
  204. let num = item.tool.toolJson.testJson.length;
  205. userAnswer.forEach((item2,index2)=>{
  206. if(type[index2]=='2'){
  207. if(JSON.stringify(userAnswer[index2].sort((a,b)=>a-b))==JSON.stringify(rightAnswer[index2].sort((a,b)=>a-b))){
  208. count++
  209. }
  210. }else if(type[index2]=='1'){
  211. if(userAnswer[index2]==rightAnswer[index2]){
  212. count++;
  213. }
  214. }
  215. })
  216. score.push((count/num).toFixed(2)*100)
  217. }else{
  218. score.push(0)
  219. }
  220. })
  221. this.accuracyList = score;
  222. this.$forceUpdate();
  223. },
  224. submitBtn(){
  225. if(this.videoType==1)return this.close();
  226. if(this.userChooseAnswer<this.json.setting.length)return this.$message.info("请答题");
  227. let data = this.json;
  228. data.setting.forEach((i,index)=>i.tool.toolJson = this.userChooseAnswer[index])
  229. let params = [{
  230. uid:this.userid,
  231. cid:this.id,
  232. stage:this.courseType,
  233. task:this.taskCount,
  234. tool:this.toolindex,
  235. content:encodeURIComponent(JSON.stringify(data)),
  236. type:14,
  237. atool:62,
  238. text:"",
  239. }]
  240. // return;
  241. this.ajax
  242. .post(this.$store.state.api + "addCourseWorks5", params)
  243. .then((res) => {
  244. this.$message.success("提交成功");
  245. this.close();
  246. })
  247. .catch((err) => {
  248. this.$message.error("提交失败");
  249. console.error(err);
  250. });
  251. },
  252. },
  253. mounted() {
  254. this.json = JSON.parse(JSON.stringify(this.videoJson));
  255. if (this.json.video) {
  256. this.playerO = JSON.parse(JSON.stringify(this.playerOptions));
  257. this.playerO.sources[0].src = this.json.video
  258. this.$nextTick(() => {
  259. setTimeout(() => {
  260. this.videoTime = Math.round(this.$refs['videoPlayer'].player.cache_.duration)
  261. }, 500)
  262. })
  263. }
  264. },
  265. }
  266. </script>
  267. <style scoped>
  268. .dialog_diy>>>.el-dialog__header {
  269. background: #3c3c3c !important;
  270. padding: 15px 20px;
  271. }
  272. .dialog_diy>>>.el-dialog__title {
  273. color: #fff;
  274. }
  275. .dialog_diy>>>.el-dialog__headerbtn {
  276. top: 19px;
  277. }
  278. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
  279. color: #fff;
  280. }
  281. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
  282. color: #fff;
  283. }
  284. .dialog_diy>>>.el-dialog__body,
  285. .dialog_diy>>>.el-dialog__footer {
  286. background: #fafafa;
  287. }
  288. .videoBox {
  289. width: 100%;
  290. height: 600px;
  291. background: #fff;
  292. margin-top: 20px;
  293. position: relative;
  294. }
  295. .chooseNodeBox{
  296. width: auto;
  297. height:50px;
  298. position:absolute;
  299. bottom:45px;
  300. left:50%;
  301. transform:translateX(-50%);
  302. display:flex;
  303. justify-content: center;
  304. align-items: center;
  305. background-color: none;
  306. }
  307. .chooseNodeBox>.chooseNodeItem{
  308. width: 40px;
  309. height: 40px;
  310. background: black;
  311. margin: 0 10px;
  312. border-radius: 50%;
  313. cursor: pointer;
  314. z-index: 9;
  315. display: flex;
  316. justify-content: center;
  317. align-items: center;
  318. color: white;
  319. font-size: 1.5em;
  320. font-weight: bold;
  321. }
  322. .videoBox>.content {
  323. /* position: absolute; */
  324. line-height: 600px;
  325. text-align: center;
  326. font-size: 18px;
  327. user-select: none;
  328. }
  329. .videoBox .video-player {
  330. height: 100%;
  331. width: auto;
  332. }
  333. .videoBox>>>.vjs-fluid {
  334. padding: 0 !important;
  335. height: 100%;
  336. }
  337. .accuracy{
  338. width: 300px;
  339. height: auto;
  340. background-color: #d4dde1;
  341. border-radius: 5px;
  342. margin: 10px 0;
  343. box-sizing: border-box;
  344. padding: 10px;
  345. }
  346. .accuracy>span{
  347. font-size: 22px;
  348. font-weight: bold;
  349. margin: 10px 0;
  350. }
  351. .accuracy>.accuracyItem{
  352. width: 100%;
  353. height: 40px;
  354. margin-top: 10px;
  355. display: flex;
  356. align-items: center;
  357. }
  358. .accuracyItem>.accuracyTitle{
  359. width: 120px;
  360. height: 100%;
  361. display: flex;
  362. justify-content: center;
  363. align-items: center;
  364. font-weight: bold;
  365. font-size:1.3em;
  366. background-color: #2c2f3b;
  367. color: white;
  368. border-radius: 5px;
  369. }
  370. .accuracyItem>.accuracyValue{
  371. width:120px;
  372. margin-left: 20px;
  373. font-size: 16px;
  374. box-sizing: border-box;
  375. padding: 5px;
  376. border-radius: 5px;
  377. border:solid 1px #0061FF;
  378. color:#0061FF;
  379. }
  380. </style>