coursewarePlay.vue 25 KB

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