coursewarePlay.vue 26 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. :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}#/?id=${
  177. activeCourseware.value?.content
  178. }&Authorization=${getToken()}&platform=pc&isHideBack=true&isHideMusicList=true&isYjt=1&systemType=teacher`,
  179. KLX: `${URL_TEACH_KLX}#/?id=${
  180. activeCourseware.value?.content
  181. }&Authorization=${getToken()}&platform=pc&isHideBack=true&isHideMusicList=true&isYjt=1&systemType=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. handleVideoPause()
  305. handleSongPause()
  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. // 取消暂停的时候收起
  437. // if (fileType.value === "VIDEO" && videoPlayDom.value?.playType === "pause") {
  438. // return
  439. // }
  440. isShowController.value = false
  441. }
  442. /* 结束课程 */
  443. function handleGoBack() {
  444. // window.open("about:blank", "_self")
  445. // window.close()
  446. router.go(-1)
  447. }
  448. function handleCoursewareEnd() {
  449. if (route.query.modeId) {
  450. if (userStoreHook.roles === "GYM") {
  451. httpAjaxLoadingErrMsg(getRecentCourseSchedule_gym, route.query.modeId as string).then(res => {
  452. if (res.code === 200) {
  453. if (res.data?.signOutStatusEnum === 3) {
  454. useDialogConfirm({
  455. headImg: require("@/img/coursewarePlay/ts.png"),
  456. text: `请确认是否结束课程,结束后请到APP进行签退。`,
  457. btnShow: [true, true],
  458. onOk() {
  459. handleGoBack()
  460. }
  461. })
  462. } else {
  463. handleGoBack()
  464. }
  465. }
  466. })
  467. } else if (userStoreHook.roles === "GYT") {
  468. httpAjaxLoadingErrMsg(checkWebCourse_gyt, route.query.modeId as string).then(res => {
  469. if (res.code === 200) {
  470. if (res.data?.signOut === false) {
  471. useDialogConfirm({
  472. headImg: require("@/img/coursewarePlay/ts.png"),
  473. text: `请确认是否结束课程,结束后请到APP进行签退。`,
  474. btnShow: [true, true],
  475. onOk() {
  476. handleGoBack()
  477. }
  478. })
  479. } else {
  480. handleGoBack()
  481. }
  482. }
  483. })
  484. }
  485. } else {
  486. handleGoBack()
  487. }
  488. }
  489. // 是否收起
  490. watch(
  491. () => isShowController.value,
  492. () => {
  493. if (isShowController.value) {
  494. isPlay.value = false
  495. } else {
  496. isPlay.value = true
  497. toolOpen.value = false
  498. }
  499. }
  500. )
  501. // 白板的批注打开时暂停播放
  502. watch(
  503. () => [whitePenShow.value, penShow.value],
  504. () => {
  505. if (whitePenShow.value || penShow.value) {
  506. handleVideoPause()
  507. handleSongPause()
  508. }
  509. }
  510. )
  511. // 去练习
  512. const activeCoursewareResourceId = computed<string | undefined>(() => {
  513. const materialRefs = activeCourseware.value?.materialRefs
  514. return materialRefs ? (["GYM", "KLX"].includes(userStoreHook.roles!) ? materialRefs[0]?.resourceIdStr : materialRefs[0]?.resourceId) : undefined
  515. })
  516. const isPracticeShow = ref(false)
  517. const practiceUrl = ref("")
  518. function handleGoPracticeBtn(activeCoursewareResourceId: string) {
  519. // GYM,GYT,KLX 区分 云教练
  520. const urlObj = {
  521. GYT: `${URL_TEACH_GYT}?id=${activeCoursewareResourceId}&modelType=practice&modeType=json&Authorization=${getToken()}&isYjt=1&&isHideBack=false`,
  522. GYM: `${URL_TEACH_GYM}#/?id=${activeCoursewareResourceId}&Authorization=${getToken()}&platform=pc&isYjt=1&systemType=teacher&isHideMusicList=true`,
  523. KLX: `${URL_TEACH_KLX}#/?id=${activeCoursewareResourceId}&Authorization=${getToken()}&platform=pc&isYjt=1&systemType=teacher&isHideMusicList=true`
  524. }
  525. isPracticeShow.value = true
  526. practiceUrl.value = urlObj[userStoreHook.roles!]
  527. //window.open(urlObj[userStoreHook.roles!], "_blank")
  528. }
  529. function handlePracticeClose() {
  530. isPracticeShow.value = false
  531. practiceUrl.value = ""
  532. }
  533. function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
  534. useCoursewarePlayTip({
  535. headImg: require(`@/img/coursewarePlay/${type === "phaseGoals" ? "ts3" : "ts4"}.png`),
  536. text,
  537. btnShow: [false, false]
  538. })
  539. handleVideoPause()
  540. }
  541. </script>
  542. <style lang="scss" scoped>
  543. .coursewarePlay {
  544. width: 100%;
  545. height: 100%;
  546. position: relative;
  547. overflow: hidden;
  548. &.hideController {
  549. .leftTools {
  550. opacity: 0;
  551. transform: translate(-100%, -50%);
  552. }
  553. .topTools {
  554. opacity: 0;
  555. transform: translateY(-100%);
  556. }
  557. .goPracticeBtn {
  558. transform: translateY(74px);
  559. }
  560. }
  561. &.fileType_song.hideController {
  562. .leftTools {
  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. display: flex;
  630. align-items: center;
  631. p {
  632. line-height: 1;
  633. padding-top: 1px;
  634. }
  635. span {
  636. background: rgba(0, 0, 0, 0.1);
  637. border-radius: 16px;
  638. border: 1px solid rgba(255, 255, 255, 0.7);
  639. font-size: 14px;
  640. color: #ffffff;
  641. line-height: 22px;
  642. padding: 2px 10px;
  643. margin-left: 10px;
  644. cursor: pointer;
  645. }
  646. }
  647. }
  648. }
  649. .midMenu {
  650. position: absolute;
  651. left: 50%;
  652. top: 23px;
  653. // transform: translate(-50%, -50%);
  654. transform: translateX(-50%);
  655. }
  656. .rightMenu {
  657. .posCloseBtn {
  658. cursor: pointer;
  659. img {
  660. width: 34px;
  661. height: 34px;
  662. padding: 6px;
  663. box-sizing: content-box;
  664. &:hover {
  665. background-color: rgba(255, 255, 255, 0.2);
  666. border-radius: 6px;
  667. }
  668. }
  669. }
  670. }
  671. }
  672. .posTools {
  673. position: absolute;
  674. top: 50%;
  675. transform: translateY(-50%);
  676. transition: all 0.5s;
  677. &.leftTools {
  678. background: rgba(0, 0, 0, 0.4);
  679. border-radius: 8px;
  680. left: 32px;
  681. }
  682. .posBtn {
  683. padding: 14px 6px;
  684. font-weight: 500;
  685. font-size: 16px;
  686. color: #ffffff;
  687. display: flex;
  688. flex-direction: column;
  689. align-items: center;
  690. cursor: pointer;
  691. // &:hover {
  692. // opacity: $opacity-hover;
  693. // }
  694. &.disabled {
  695. opacity: $opacity-disabled;
  696. }
  697. > img {
  698. width: 34px;
  699. height: 34px;
  700. padding: 6px;
  701. box-sizing: content-box;
  702. &:hover {
  703. background-color: rgba(255, 255, 255, 0.2);
  704. border-radius: 6px;
  705. }
  706. }
  707. }
  708. }
  709. .goPracticeBtn {
  710. position: absolute;
  711. right: 32px;
  712. bottom: 24px;
  713. width: 143px;
  714. height: 50px;
  715. background: url("@/img/coursewarePlay/goPracticeBtn.png") no-repeat;
  716. background-size: 100% 100%;
  717. cursor: pointer;
  718. transition: all 0.5s;
  719. &:hover {
  720. opacity: $opacity-hover;
  721. }
  722. }
  723. &:deep(.elDrawer.el-drawer) {
  724. width: 348px !important;
  725. .el-drawer__header {
  726. height: 54px;
  727. background: #ededed;
  728. padding: 0 20px;
  729. margin-bottom: 0;
  730. .directory {
  731. flex-grow: 0;
  732. flex-shrink: 0;
  733. width: 24px;
  734. height: 24px;
  735. }
  736. .tit {
  737. flex-grow: 1;
  738. margin-left: 10px;
  739. font-weight: 600;
  740. font-size: 18px;
  741. color: #333333;
  742. }
  743. .close {
  744. cursor: pointer;
  745. width: 14px;
  746. flex-shrink: 0;
  747. &:hover {
  748. opacity: $opacity-hover;
  749. }
  750. }
  751. }
  752. .el-drawer__body {
  753. padding: 0;
  754. overflow: hidden;
  755. & > .elScrollbar {
  756. .el-scrollbar__view {
  757. padding: 0 22px;
  758. width: 100%;
  759. }
  760. .el-scrollbar__wrap {
  761. overflow-x: hidden;
  762. }
  763. }
  764. }
  765. }
  766. &:deep(.elCourseMenu.el-drawer) {
  767. width: 363px !important;
  768. .el-drawer__body {
  769. & > .elScrollbar {
  770. .el-scrollbar__view {
  771. padding: 0;
  772. }
  773. }
  774. }
  775. }
  776. }
  777. </style>