coursewarePlay.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. <!--
  2. * @FileDescription: 教程播放
  3. * @Author: 黄琪勇
  4. * @Date:2024-04-03 17:31:41
  5. -->
  6. <template>
  7. <div class="coursewarePlay" :class="[!isShowController && 'hideController', fileType === 'SONG' && 'fileType_song']">
  8. <div class="coursewarePlayCon" @mousemove="handleMousemove" @click="handleClick" @touchstart="handleClick">
  9. <videoPlay
  10. v-show="fileType === 'VIDEO'"
  11. ref="videoPlayDom"
  12. @ended="handleChangeCourseware(1)"
  13. @loadedmetadata="
  14. () => {
  15. isTempAutoPlay = false
  16. }
  17. "
  18. :autoPlay="true"
  19. @playbackRate="showController"
  20. :disableEvents="true"
  21. :isShowController="isShowController"
  22. />
  23. <div class="imgPlayBox" v-if="fileType === 'IMG'">
  24. <ElImage :hide-on-click-modal="true" fit="contain" :src="activeCourseware?.content" class="imgPlay" />
  25. </div>
  26. <div class="songPlayBox" v-if="fileType === 'SONG'">
  27. <iframe ref="songPlayDom" class="songIframe" @mousemove="handleMousemove" :src="songPlaySrc" frameborder="0"></iframe>
  28. </div>
  29. </div>
  30. <div class="leftTools posTools">
  31. <div class="posBtn" @click="handleToolClick('menu')">
  32. <img src="@/img/coursewarePlay/menu.png" />
  33. <!-- <div>课程类型</div> -->
  34. </div>
  35. <div class="posBtn" @click="handleToolClick('point')">
  36. <img src="@/img/coursewarePlay/zhishidian.png" />
  37. <!-- <div>知识点</div> -->
  38. </div>
  39. <div
  40. :class="['posBtn', activeCoursewareIndex > 0 ? '' : 'disabled']"
  41. @click="
  42. () => {
  43. if (activeCoursewareIndex > 0) {
  44. handleChangeCourseware(-1)
  45. }
  46. }
  47. "
  48. >
  49. <img src="@/img/coursewarePlay/shang.png" />
  50. <!-- <div>上一个</div> -->
  51. </div>
  52. <div
  53. :class="['posBtn', activeCoursewareIndex < flattenCoursewareList.length - 1 ? '' : 'disabled']"
  54. @click="
  55. () => {
  56. if (activeCoursewareIndex < flattenCoursewareList.length - 1) {
  57. handleChangeCourseware(1)
  58. }
  59. }
  60. "
  61. >
  62. <img src="@/img/coursewarePlay/xia.png" />
  63. <!-- <div>下一个</div> -->
  64. </div>
  65. </div>
  66. <div
  67. v-if="activeCoursewareResourceId"
  68. @click="
  69. () => {
  70. handleVideoPause()
  71. handleGoPracticeBtn(activeCoursewareResourceId!)
  72. }
  73. "
  74. class="goPracticeBtn"
  75. ></div>
  76. <div class="topTools">
  77. <div class="leftMenu">
  78. <img @click="handleGoBack" class="backImg" src="@/img/coursewarePlay/back.png" />
  79. <div class="title-section">
  80. <div class="title">{{ activeCourseware?.parentData.name || "" }}</div>
  81. <div class="content">
  82. <p>{{ activeCourseware?.name || "" }}</p>
  83. <!-- <span v-if="activeCourseware?.phaseGoals" @click="onTitleTip('phaseGoals', activeCourseware?.phaseGoals)">阶段目标</span> -->
  84. <span v-if="lessonTargetDetail" @click="onTitleTip('phaseGoals', lessonTargetDetail)">阶段目标</span>
  85. <span v-if="activeCourseware?.checkItem" @click="onTitleTip('checkItem', activeCourseware?.checkItem)">检查事项</span>
  86. </div>
  87. </div>
  88. </div>
  89. <div class="midMenu">
  90. <playRecordTime
  91. v-if="route.query.modeId && coursewareTotalTime && userStoreHook.roles === 'GYT'"
  92. :modeId="route.query.modeId as string"
  93. :isCurrentCoursewareMenu="isCurrentCoursewareMenu"
  94. :coursewareTotalTime="coursewareTotalTime"
  95. />
  96. </div>
  97. <div class="rightMenu">
  98. <div class="posCloseBtn" @click="handleCoursewareEnd">
  99. <img src="@/img/coursewarePlay/jieshu.png" />
  100. </div>
  101. </div>
  102. </div>
  103. <el-drawer class="elDrawer" direction="ltr" v-model="drawerShow" :show-close="false">
  104. <template #header="{ close }">
  105. <img class="directory" src="@/img/coursewarePlay/kcml.png" />
  106. <div class="tit">知识点目录</div>
  107. <img class="close" @click="close" src="@/img/coursewarePlay/close.png" />
  108. </template>
  109. <ElScrollbar class="elScrollbar">
  110. <courseCollapse :activeCollapse="activeCourseware" :courseList="coursewareList" @handleClick="handleCourseClick" />
  111. </ElScrollbar>
  112. </el-drawer>
  113. <el-drawer class="elDrawer elCourseMenu" direction="ltr" v-model="drawerMenuShow" :show-close="false">
  114. <template #header="{ close }">
  115. <img class="directory" src="@/img/coursewarePlay/menuActive.png" />
  116. <div class="tit">课程类型</div>
  117. <img class="close" @click="close" src="@/img/coursewarePlay/close.png" />
  118. </template>
  119. <ElScrollbar class="elScrollbar">
  120. <courseMenuCollapse :courseMenuList="coursewareMenuList" @handleClick="handleCourseMenuClick" />
  121. </ElScrollbar>
  122. </el-drawer>
  123. <practiceForm v-model="isPracticeShow" :practiceUrl="practiceUrl" @close="handlePracticeClose" />
  124. </div>
  125. </template>
  126. <script setup lang="ts">
  127. import videoPlay from "./videoPlay"
  128. import { getLessonCourseDetail_gym, getLessonCoursewareDetail_gyt, getLessonCourseDetail_klx } from "@/api/cloudTextbooks.api"
  129. import { checkWebCourse_gyt, refLevel_gym, refLevel_gyt, refLevel_klx } from "@/api/coursewarePlay.api"
  130. import { httpAjaxErrMsg, httpAjaxLoadingErrMsg } from "@/plugin/httpAjax"
  131. import userStore from "@/store/modules/user"
  132. import { useRoute, useRouter } from "vue-router"
  133. import { shallowRef, ref, computed, onUnmounted, onMounted, watch, nextTick } from "vue"
  134. import { ElMessageBox } from "element-plus"
  135. import courseCollapse from "./components/courseCollapse"
  136. import courseMenuCollapse from "./components/courseMenuCollapse"
  137. import playRecordTime from "./components/playRecordTime"
  138. import practiceForm from "@/businessComponents/practiceForm"
  139. import { getRecentCourseSchedule_gym } from "@/api/homePage.api"
  140. import { getToken } from "@/libs/auth"
  141. import { URL_TEACH_GYT, URL_TEACH_GYM, URL_TEACH_KLX } from "@/config"
  142. import { handleFullscreen } from "@/libs/fullscreen"
  143. import useCoursewarePlayTip from "./components/useCoursewarePlayTip"
  144. import useDialogConfirm from "@/hooks/useDialogConfirm"
  145. import LoadingBar from "@/plugin/loadingBar"
  146. import { isPlay, penShow, toolOpen, whitePenShow } from "@/businessComponents/globalTools/globalTools"
  147. import { closeAllModalFrame } from "@/plugin/modalFrame"
  148. const route = useRoute()
  149. const router = useRouter()
  150. const userStoreHook = userStore()
  151. /* 获取资源 */
  152. const videoPlayDom = ref<InstanceType<typeof videoPlay>>()
  153. const songPlayDom = ref<any>() // 曲谱对象
  154. const lessonTargetDetail = ref<string>("") // 阶段目标
  155. const coursewareMenuList = shallowRef<any[]>([]) // 课程类型
  156. const coursewareList = shallowRef<any[]>([]) // 知识点
  157. const flattenCoursewareList = ref<any[]>([]) // 扁平化coursewareList
  158. const isCurrentCoursewareMenu = shallowRef(true) // 是否为当前选的课程类型
  159. const currentId = ref<any>(route.params.id)
  160. const isTempAutoPlay = ref(false)
  161. // 选中的知识点
  162. const activeCourseware = computed<undefined | Record<string, any>>(() => {
  163. return flattenCoursewareList.value[activeCoursewareIndex.value]
  164. })
  165. // 文件类型
  166. const fileType = computed<"VIDEO" | "IMG" | "SONG">(() => {
  167. return activeCourseware.value?.typeCode || activeCourseware.value?.type
  168. })
  169. const songPlaySrc = computed<string>(() => {
  170. if (fileType.value !== "SONG") {
  171. return ""
  172. }
  173. // GYM,GYT,KLX 区分 云教练
  174. const urlObj = {
  175. GYT: `${URL_TEACH_GYT}?id=${activeCourseware.value?.content}&modelType=practice&modeType=json&Authorization=${getToken()}&isYjt=1`,
  176. GYM: `${URL_TEACH_GYM}#/detail/${
  177. activeCourseware.value?.content
  178. }?Authorization=${getToken()}&platform=web&liveConfig=1&isHideBack=true&isYjt=1`,
  179. KLX: `${URL_TEACH_KLX}?Authorization=${getToken()}&id=${
  180. activeCourseware.value?.content
  181. }&isHideBack=true&limitModel=practice&isYjt=1&client=teacher`
  182. }
  183. return urlObj[userStoreHook.roles!]
  184. })
  185. // 视频是否自动播放
  186. // const videoIsAutoPlay = computed<boolean>(() => {
  187. // // 如果为视频且有阶段目前则不自动播放
  188. // console.log(fileType.value, isTempAutoPlay.value, "isTempAutoPlay")
  189. // return (fileType.value === "VIDEO" && activeCourseware.value?.phaseGoals) || isTempAutoPlay.value ? false : true
  190. // })
  191. const activeCoursewareIndex = ref(0)
  192. const drawerShow = ref(false)
  193. const drawerMenuShow = ref(false)
  194. // 课程总时间
  195. const coursewareTotalTime = ref(0)
  196. // 监控播放
  197. watch(activeCourseware, () => {
  198. fileType.value === "VIDEO" &&
  199. nextTick(() => {
  200. handlePlayVideo({
  201. src: activeCourseware.value?.content,
  202. name: activeCourseware.value?.name
  203. })
  204. })
  205. showController()
  206. })
  207. getCoursewareList()
  208. async function getCoursewareList(id?: string) {
  209. // GYM,GYT,KLX 区分 查询接口
  210. const LessonCoursewareDetailApi = {
  211. GYT: getLessonCoursewareDetail_gyt,
  212. GYM: getLessonCourseDetail_gym,
  213. KLX: getLessonCourseDetail_klx
  214. }
  215. await httpAjaxErrMsg(LessonCoursewareDetailApi[userStoreHook.roles!], id || (route.params.id as string)).then(res => {
  216. if (res.code === 200) {
  217. const { lockFlag, knowledgePointList, lessonTargetDesc } = res.data || {}
  218. if (lockFlag) {
  219. ElMessageBox.alert("课件已锁定", "温馨提示", {
  220. confirmButtonText: "退出",
  221. type: "error"
  222. })
  223. .then(() => {
  224. handleGoBack()
  225. })
  226. .catch(() => {
  227. handleGoBack()
  228. })
  229. return
  230. }
  231. if ((knowledgePointList || []).length < 1) {
  232. ElMessageBox.alert("没有找到课件", "温馨提示", {
  233. confirmButtonText: "退出",
  234. type: "error"
  235. })
  236. .then(() => {
  237. handleGoBack()
  238. })
  239. .catch(() => {
  240. handleGoBack()
  241. })
  242. return
  243. }
  244. lessonTargetDetail.value = lessonTargetDesc ? lessonTargetDesc.replace(/\n/g, "<br />") : ""
  245. // 处理返回的数据
  246. handlePointList(knowledgePointList)
  247. }
  248. })
  249. }
  250. getCoursewareMenuList()
  251. function getCoursewareMenuList(id?: string) {
  252. // GYM,GYT,KLX 区分 查询接口
  253. const LessonCoursewareMenuDetailApi = {
  254. GYT: refLevel_gyt,
  255. GYM: refLevel_gym,
  256. KLX: refLevel_klx
  257. }
  258. httpAjaxErrMsg(LessonCoursewareMenuDetailApi[userStoreHook.roles!], {
  259. lessonCoursewareDetailId: id || route.params.id,
  260. courseScheduleId: route.query.modeId as any
  261. } as any).then(res => {
  262. if (res.code === 200) {
  263. coursewareMenuList.value = res.data || []
  264. }
  265. })
  266. }
  267. let flattenCoursewareListData: any = [] // 临时扁平化数据
  268. function handlePointList(pointList: any[]) {
  269. // 重置数据
  270. coursewareTotalTime.value = 0
  271. coursewareList.value = filterPointList(pointList)
  272. // 如果url里面有materialId 代表指定资料播放
  273. if (route.query.materialId) {
  274. const index = flattenCoursewareListData.findIndex((item: any) => {
  275. return route.query.materialId === item.id + "" && route.query.knowledgePointId === item.knowledgePointId + ""
  276. })
  277. index > -1 && (activeCoursewareIndex.value = index)
  278. }
  279. flattenCoursewareList.value = flattenCoursewareListData
  280. }
  281. function filterPointList(pointList: any[], parentData?: { ids: string[]; name: string }): any[] {
  282. // 设置父级及以上id数组和父级name
  283. return pointList.map(point => {
  284. if (point.children) {
  285. return Object.assign(point, {
  286. children: filterPointList(point.children, { ids: [...(parentData?.ids || []), point.id], name: point.name })
  287. })
  288. } else {
  289. coursewareTotalTime.value += point.totalMaterialTimeSecond
  290. return Object.assign(point, {
  291. materialList: point.materialList.map((item: any) => {
  292. item.parentData = {
  293. ids: [...(parentData?.ids || []), point.id],
  294. name: point.name
  295. }
  296. flattenCoursewareListData.push(item)
  297. return item
  298. })
  299. })
  300. }
  301. })
  302. }
  303. function handleChangeCourseware(index: -1 | 1) {
  304. const newIndex = index + activeCoursewareIndex.value
  305. if (newIndex < 0 || newIndex > flattenCoursewareList.value.length - 1) {
  306. return
  307. }
  308. activeCoursewareIndex.value = newIndex
  309. }
  310. function handleCourseClick(value: any) {
  311. activeCoursewareIndex.value = flattenCoursewareList.value.findIndex((item: any) => {
  312. return value.id === item.id && value.knowledgePointId === item.knowledgePointId
  313. })
  314. drawerShow.value = false
  315. }
  316. async function handleCourseMenuClick(value: any) {
  317. // 判断是否为当前课程类型
  318. if (currentId.value === value.id) {
  319. return
  320. }
  321. LoadingBar.loading(true)
  322. currentId.value = value.id
  323. isCurrentCoursewareMenu.value = value.id === route.params.id ? true : false
  324. flattenCoursewareListData = [] // 重置数据
  325. isTempAutoPlay.value = true
  326. await getCoursewareList(value.id)
  327. getCoursewareMenuList(value.id)
  328. drawerMenuShow.value = false
  329. activeCoursewareIndex.value = 0
  330. nextTick(() => {
  331. // if (!activeCourseware.value?.phaseGoals) {
  332. // 切换之后默认打开课程目录
  333. drawerShow.value = true
  334. // }
  335. })
  336. LoadingBar.loading(false)
  337. }
  338. /** 曲目相关 */
  339. // 暂停曲目播放
  340. function handleSongPause() {
  341. songPlayDom.value?.contentWindow?.postMessage({ api: "setPlayState" }, "*")
  342. showController()
  343. }
  344. /* 播放器相关 */
  345. // 视频播放或者暂停
  346. function handleVideoPlay() {
  347. videoPlayDom.value?.handlePlay()
  348. showController()
  349. }
  350. // 视频快进快退
  351. function handleVideoSpeedCurrentTime(type: "fast" | "slow") {
  352. videoPlayDom.value?.speedCurrentTime(type)
  353. showController()
  354. }
  355. // 视频暂停
  356. function handleVideoPause() {
  357. videoPlayDom.value?.pauseVideo()
  358. showController()
  359. }
  360. // 播放视频
  361. function handlePlayVideo({ src, name }: { src: string; name: string }) {
  362. videoPlayDom.value?.playVideo({
  363. src,
  364. name
  365. })
  366. showController()
  367. }
  368. // 全屏显示
  369. handleFullscreen(true, false)
  370. /* 按键事件相关 */
  371. onMounted(() => {
  372. document.addEventListener("keydown", handleKeydown)
  373. document.addEventListener("contextmenu", preventDefaultContextmenu)
  374. showController()
  375. })
  376. onUnmounted(() => {
  377. document.removeEventListener("keydown", handleKeydown)
  378. document.removeEventListener("contextmenu", preventDefaultContextmenu)
  379. })
  380. function preventDefaultContextmenu(event: MouseEvent) {
  381. event.preventDefault()
  382. }
  383. function handleKeydown(e: KeyboardEvent) {
  384. const key = e.key
  385. if (key === " ") {
  386. closeAllModalFrame()
  387. drawerShow.value = false
  388. // 视频类型的时候才触发
  389. fileType.value === "VIDEO" && handleVideoPlay()
  390. } else if (key === "ArrowLeft") {
  391. closeAllModalFrame()
  392. drawerShow.value = false
  393. // 视频类型的时候才触发
  394. fileType.value === "VIDEO" && handleVideoSpeedCurrentTime("slow")
  395. } else if (key === "ArrowRight") {
  396. closeAllModalFrame()
  397. drawerShow.value = false
  398. // 视频类型的时候才触发
  399. fileType.value === "VIDEO" && handleVideoSpeedCurrentTime("fast")
  400. } else if (key === "ArrowDown") {
  401. closeAllModalFrame()
  402. drawerShow.value = false
  403. handleChangeCourseware(1)
  404. } else if (key === "ArrowUp") {
  405. closeAllModalFrame()
  406. drawerShow.value = false
  407. handleChangeCourseware(-1)
  408. }
  409. }
  410. function handleToolClick(type: string) {
  411. fileType.value === "VIDEO" && handleVideoPause()
  412. if (type === "menu") {
  413. drawerMenuShow.value = true
  414. } else if (type === "point") {
  415. drawerShow.value = true
  416. }
  417. }
  418. function handleMousemove() {
  419. showController()
  420. }
  421. function handleClick() {
  422. fileType.value === "VIDEO" && isShowController.value && handleVideoPlay()
  423. showController()
  424. }
  425. // 是否显示控制器
  426. const isShowController = ref(true)
  427. let _showTimer: any
  428. function showController() {
  429. isShowController.value = true
  430. _showTimer && clearTimeout(_showTimer)
  431. _showTimer = setTimeout(hideController, 3000)
  432. }
  433. function hideController() {
  434. if (fileType.value === "VIDEO" && videoPlayDom.value?.playType === "pause") {
  435. return
  436. }
  437. isShowController.value = false
  438. }
  439. /* 结束课程 */
  440. function handleGoBack() {
  441. // window.open("about:blank", "_self")
  442. // window.close()
  443. router.go(-1)
  444. }
  445. function handleCoursewareEnd() {
  446. if (route.query.modeId) {
  447. if (userStoreHook.roles === "GYM") {
  448. httpAjaxLoadingErrMsg(getRecentCourseSchedule_gym, route.query.modeId as string).then(res => {
  449. if (res.code === 200) {
  450. if (res.data?.signOutStatusEnum === 3) {
  451. useDialogConfirm({
  452. headImg: require("@/img/coursewarePlay/ts.png"),
  453. text: `请确认是否结束课程,结束后请到APP进行签退。`,
  454. btnShow: [true, true],
  455. onOk() {
  456. handleGoBack()
  457. }
  458. })
  459. } else {
  460. handleGoBack()
  461. }
  462. }
  463. })
  464. } else if (userStoreHook.roles === "GYT") {
  465. httpAjaxLoadingErrMsg(checkWebCourse_gyt, route.query.modeId as string).then(res => {
  466. if (res.code === 200) {
  467. if (res.data?.signOut === false) {
  468. useDialogConfirm({
  469. headImg: require("@/img/coursewarePlay/ts.png"),
  470. text: `请确认是否结束课程,结束后请到APP进行签退。`,
  471. btnShow: [true, true],
  472. onOk() {
  473. handleGoBack()
  474. }
  475. })
  476. } else {
  477. handleGoBack()
  478. }
  479. }
  480. })
  481. }
  482. } else {
  483. handleGoBack()
  484. }
  485. }
  486. // 是否收起
  487. watch(
  488. () => isShowController.value,
  489. () => {
  490. if (isShowController.value) {
  491. isPlay.value = false
  492. } else {
  493. isPlay.value = true
  494. toolOpen.value = false
  495. }
  496. }
  497. )
  498. // 白板的批注打开时暂停播放
  499. watch(
  500. () => [whitePenShow.value, penShow.value],
  501. () => {
  502. if (whitePenShow.value || penShow.value) {
  503. handleVideoPause()
  504. handleSongPause()
  505. }
  506. }
  507. )
  508. // 去练习
  509. const activeCoursewareResourceId = computed<string | undefined>(() => {
  510. const materialRefs = activeCourseware.value?.materialRefs
  511. return materialRefs ? (["GYM", "KLX"].includes(userStoreHook.roles!) ? materialRefs[0]?.resourceIdStr : materialRefs[0]?.resourceId) : undefined
  512. })
  513. const isPracticeShow = ref(false)
  514. const practiceUrl = ref("")
  515. function handleGoPracticeBtn(activeCoursewareResourceId: string) {
  516. // GYM,GYT,KLX 区分 云教练
  517. const urlObj = {
  518. GYT: `${URL_TEACH_GYT}?id=${activeCoursewareResourceId}&modelType=practice&modeType=json&Authorization=${getToken()}&isYjt=1&&isHideBack=false`,
  519. GYM: `${URL_TEACH_GYM}#/detail/${activeCoursewareResourceId}?Authorization=${getToken()}&platform=web&liveConfig=1&isYjt=1`,
  520. KLX: `${URL_TEACH_KLX}?Authorization=${getToken()}&id=${activeCoursewareResourceId}&limitModel=practice&isYjt=1&client=teacher`
  521. }
  522. isPracticeShow.value = true
  523. practiceUrl.value = urlObj[userStoreHook.roles!]
  524. //window.open(urlObj[userStoreHook.roles!], "_blank")
  525. }
  526. function handlePracticeClose() {
  527. isPracticeShow.value = false
  528. practiceUrl.value = ""
  529. }
  530. function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
  531. useCoursewarePlayTip({
  532. headImg: require(`@/img/coursewarePlay/${type === "phaseGoals" ? "ts3" : "ts4"}.png`),
  533. text,
  534. btnShow: [false, false]
  535. })
  536. handleVideoPause()
  537. }
  538. </script>
  539. <style lang="scss" scoped>
  540. .coursewarePlay {
  541. width: 100%;
  542. height: 100%;
  543. position: relative;
  544. overflow: hidden;
  545. &.hideController {
  546. .leftTools {
  547. opacity: 0;
  548. transform: translate(-100%, -50%);
  549. }
  550. .topTools {
  551. opacity: 0;
  552. transform: translateY(-100%);
  553. }
  554. .goPracticeBtn {
  555. transform: translateY(74px);
  556. }
  557. }
  558. &.fileType_song.hideController {
  559. .leftTools {
  560. opacity: initial;
  561. transform: translateY(-50%);
  562. }
  563. .goPracticeBtn {
  564. transform: initial;
  565. }
  566. }
  567. .coursewarePlayCon {
  568. width: 100%;
  569. height: 100%;
  570. overflow: hidden;
  571. .imgPlayBox {
  572. width: 100%;
  573. height: 100%;
  574. display: flex;
  575. justify-content: center;
  576. align-items: center;
  577. .imgPlay {
  578. width: 84%;
  579. height: 100%;
  580. }
  581. }
  582. .songPlayBox {
  583. width: 100%;
  584. height: 100%;
  585. .songIframe {
  586. display: block;
  587. width: 100%;
  588. height: 100%;
  589. }
  590. }
  591. }
  592. .topTools {
  593. position: absolute;
  594. top: 0;
  595. left: 0;
  596. width: 100%;
  597. background: linear-gradient(180deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%);
  598. transition: all 0.5s;
  599. display: flex;
  600. align-items: flex-start;
  601. justify-content: space-between;
  602. padding: 20px 30px 40px;
  603. .leftMenu {
  604. display: flex;
  605. align-items: flex-start;
  606. .backImg {
  607. cursor: pointer;
  608. width: 22px;
  609. padding-top: 5px;
  610. &:hover {
  611. opacity: $opacity-hover;
  612. }
  613. }
  614. .title-section {
  615. font-weight: 500;
  616. font-size: 22px;
  617. color: #ffffff;
  618. line-height: 30px;
  619. padding-left: 20px;
  620. .content {
  621. padding-top: 6px;
  622. font-weight: 500;
  623. font-size: 18px;
  624. color: #ffffff;
  625. line-height: 26px;
  626. display: flex;
  627. align-items: center;
  628. p {
  629. line-height: 1;
  630. padding-top: 1px;
  631. }
  632. span {
  633. background: rgba(0, 0, 0, 0.1);
  634. border-radius: 16px;
  635. border: 1px solid rgba(255, 255, 255, 0.7);
  636. font-size: 14px;
  637. color: #ffffff;
  638. line-height: 22px;
  639. padding: 2px 10px;
  640. margin-left: 10px;
  641. cursor: pointer;
  642. }
  643. }
  644. }
  645. }
  646. .midMenu {
  647. position: absolute;
  648. left: 50%;
  649. top: 23px;
  650. // transform: translate(-50%, -50%);
  651. transform: translateX(-50%);
  652. }
  653. .rightMenu {
  654. .posCloseBtn {
  655. cursor: pointer;
  656. img {
  657. width: 34px;
  658. height: 34px;
  659. padding: 6px;
  660. box-sizing: content-box;
  661. &:hover {
  662. background-color: rgba(255, 255, 255, 0.2);
  663. border-radius: 6px;
  664. }
  665. }
  666. }
  667. }
  668. }
  669. .posTools {
  670. position: absolute;
  671. top: 50%;
  672. transform: translateY(-50%);
  673. transition: all 0.5s;
  674. &.leftTools {
  675. background: rgba(0, 0, 0, 0.4);
  676. border-radius: 8px;
  677. left: 32px;
  678. }
  679. .posBtn {
  680. padding: 14px 6px;
  681. font-weight: 500;
  682. font-size: 16px;
  683. color: #ffffff;
  684. display: flex;
  685. flex-direction: column;
  686. align-items: center;
  687. cursor: pointer;
  688. // &:hover {
  689. // opacity: $opacity-hover;
  690. // }
  691. &.disabled {
  692. opacity: $opacity-disabled;
  693. }
  694. > img {
  695. width: 34px;
  696. height: 34px;
  697. padding: 6px;
  698. box-sizing: content-box;
  699. &:hover {
  700. background-color: rgba(255, 255, 255, 0.2);
  701. border-radius: 6px;
  702. }
  703. }
  704. }
  705. }
  706. .goPracticeBtn {
  707. position: absolute;
  708. right: 32px;
  709. bottom: 24px;
  710. width: 143px;
  711. height: 50px;
  712. background: url("@/img/coursewarePlay/goPracticeBtn.png") no-repeat;
  713. background-size: 100% 100%;
  714. cursor: pointer;
  715. transition: all 0.5s;
  716. &:hover {
  717. opacity: $opacity-hover;
  718. }
  719. }
  720. &:deep(.elDrawer.el-drawer) {
  721. width: 348px !important;
  722. .el-drawer__header {
  723. height: 54px;
  724. background: #ededed;
  725. padding: 0 20px;
  726. margin-bottom: 0;
  727. .directory {
  728. flex-grow: 0;
  729. flex-shrink: 0;
  730. width: 24px;
  731. height: 24px;
  732. }
  733. .tit {
  734. flex-grow: 1;
  735. margin-left: 10px;
  736. font-weight: 600;
  737. font-size: 18px;
  738. color: #333333;
  739. }
  740. .close {
  741. cursor: pointer;
  742. width: 14px;
  743. flex-shrink: 0;
  744. &:hover {
  745. opacity: $opacity-hover;
  746. }
  747. }
  748. }
  749. .el-drawer__body {
  750. padding: 0;
  751. overflow: hidden;
  752. & > .elScrollbar {
  753. .el-scrollbar__view {
  754. padding: 0 22px;
  755. width: 100%;
  756. }
  757. .el-scrollbar__wrap {
  758. overflow-x: hidden;
  759. }
  760. }
  761. }
  762. }
  763. &:deep(.elCourseMenu.el-drawer) {
  764. width: 363px !important;
  765. .el-drawer__body {
  766. & > .elScrollbar {
  767. .el-scrollbar__view {
  768. padding: 0;
  769. }
  770. }
  771. }
  772. }
  773. }
  774. </style>