coursewarePlay.vue 25 KB

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