detail.uvue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <script setup lang='ts'>
  2. import Back from '@/components/back.uvue'
  3. import Loading from '@/components/loading.uvue'
  4. import { ref, onMounted, getCurrentInstance } from 'vue'
  5. import { type SubjectCourseResult, fetchSubjectCourseApp, updateSubjectProgress } from '@/services/subject/course'
  6. import { router } from '@/.cool'
  7. const isLoading = ref(true)
  8. const showProgress = ref(true)
  9. const showControls = ref(true)
  10. const videoContext = ref<UniApp.VideoContext>()
  11. const data = ref({
  12. src: 'https://oss.xiaoxiongcode.com/course-1/animate/第1集_观察.mp4'
  13. })
  14. const menuItems = [
  15. { id: 'watch', name: '看课', icon: 'https://oss.xiaoxiongcode.com/static/home/kanke.png' },
  16. { id: 'practice', name: '练习', icon: 'https://oss.xiaoxiongcode.com/static/home/lianxi.png' },
  17. { id: 'experiment', name: '虚拟实验', icon: 'https://oss.xiaoxiongcode.com/static/home/shiyan.png' },
  18. { id: 'diary', name: '科学日记', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' }
  19. ]
  20. const menu2Items = [
  21. { id: '1', name: '观察', icon: 'https://oss.xiaoxiongcode.com/static/home/kanke.png' },
  22. { id: '2', name: '提问', icon: 'https://oss.xiaoxiongcode.com/static/home/lianxi.png' },
  23. { id: '3', name: '假设', icon: 'https://oss.xiaoxiongcode.com/static/home/shiyan.png' },
  24. { id: '4', name: '实验', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  25. { id: '5', name: '总结', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  26. { id: '6', name: '拓展', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  27. ]
  28. //当前进度
  29. const progress = ref(1)
  30. const progress2 = ref(1)
  31. onReady(() => {
  32. videoContext.value = uni.createVideoContext("video1", getCurrentInstance()!.proxy!)
  33. })
  34. const course = ref<SubjectCourseResult>()
  35. //通过路由参数获取课程id
  36. async function fetchCatalog() {
  37. course.value = await fetchSubjectCourseApp({ id: router.query().id })
  38. data.value.src = course.value?.detailItem?.observeVideoPath || ''
  39. if (!course.value?.courseUserProgress) {
  40. // await updateSubjectProgress({
  41. // courseId: course.value?.id,
  42. // mainProgress: 1,
  43. // assistantProgress: 1,
  44. // status: 0
  45. // })
  46. progress.value = 1
  47. progress2.value = 1
  48. } else {
  49. progress.value = course.value?.courseUserProgress.mainProgress
  50. progress2.value = course.value?.courseUserProgress.assistantProgress
  51. }
  52. }
  53. onMounted(async () => {
  54. await fetchCatalog()
  55. isLoading.value = false
  56. setTimeout(() => {
  57. showProgress.value = false
  58. }, 2000)
  59. })
  60. async function handleEnded() {
  61. await updateSubjectProgress({
  62. courseId: course.value?.id,
  63. mainProgress: 1,
  64. assistantProgress: 2,
  65. status: 0
  66. })
  67. progress2.value = 2
  68. }
  69. function handleControlsToggle(e) {
  70. console.log(e)
  71. showControls.value = e.detail.show
  72. }
  73. </script>
  74. <template>
  75. <Loading v-show="isLoading" />
  76. <cl-page v-show="!isLoading">
  77. <Back v-show="showProgress" />
  78. <view class="w-[64vw] h-[36vw] absolute top-1/2 z-[1] left-[7vw] translate50"
  79. :class="{ ' rounded-2xl p-[3px] bg-black mt-[25px] ': showProgress, 'video-container': !showProgress }">
  80. <video id="video1" class="w-full h-full " :class="{ 'rounded-2xl': showProgress }" :src="data.src"
  81. :show-center-play-btn="false" :show-background-playback-button="false" :show-fullscreen-btn="false"
  82. :show-casting-button="false" autoplay @controlstoggle="handleControlsToggle" @ended="handleEnded">
  83. </video>
  84. <view class="video-fullscreen_title" v-show="showControls">
  85. <Back v-show="!showProgress" />
  86. <view class="text-[20px] font-bold text-white">
  87. {{ course?.mainTitle }}是的
  88. </view>
  89. <view class="control-progress">
  90. <view v-for="(item, i) in menu2Items" :key="item.id"
  91. class="px-0 py-3 flex items-center justify-center gap-[5px]"
  92. :class="{ '!bg-[#fff] rounded-full': progress2 > i }">
  93. <cl-image :src="item.icon" mode="heightFix" class="!h-[30px]"></cl-image>
  94. <text class="text-[14px] font-bold" :class="{ '!text-[#2BA0F3]': progress2 == i + 1 }">{{ item.name
  95. }}</text>
  96. </view>
  97. </view>
  98. </view>
  99. </view>
  100. <view class="course-detail-page" :class="{ 'hidden': !showProgress }">
  101. <!-- 顶部标题栏 -->
  102. <view class="flex-[1] h-[100vh] relative">
  103. <view class="flex flex-row w-full pr-[3vw] pl-[10vw] items-center justify-between absolute top-[30px]">
  104. <text class="text-[5vh] font-bold">{{ course?.mainTitle }}</text>
  105. <text
  106. class="rounded-full p-[1vh] px-[2vh] border-2 border-[#fff] border-solid text-[#fff] text-[3vh] font-bold">课程收获</text>
  107. </view>
  108. </view>
  109. <!-- 右侧功能菜单 -->
  110. <view class="w-[25vw] h-[100vh] bg-[#5CBDFD] flex flex-col justify-center p-5">
  111. <view v-for="(item, i) in menuItems" :key="item.id" class="h-[20vh] flex flex-row items-center ">
  112. <view
  113. class="h-[15vh] w-[20vh] bg-[#999999] rounded-2xl border-[.5vh] border-[#254AD9] border-b-[1vh] border-solid flex items-center justify-center gap-[1vh]"
  114. :class="{ '!bg-[#fff]': progress > i }">
  115. <cl-image :src="item.icon" mode="heightFix" class="!h-[7vh]"></cl-image>
  116. <text class="text-[2vh] font-bold">{{ item.name }}</text>
  117. </view>
  118. <view class="flex items-center h-[20vh] ml-[2vh] ">
  119. <view class="w-[1vh] bg-[#B4DBF7] flex-1" :class="{ '!bg-[#71C73D]': progress > i, '!opacity-0': i === 0 }">
  120. </view>
  121. <view class="w-[5vh] bg-[#fff] h-[5vh] rounded-full" :class="{ '!bg-[#71C73D]': progress > i }"></view>
  122. <view class="w-[1vh] bg-[#B4DBF7] flex-1"
  123. :class="{ '!bg-[#71C73D]': progress > i, '!opacity-0': i === menuItems.length - 1 }"></view>
  124. </view>
  125. </view>
  126. </view>
  127. </view>
  128. </cl-page>
  129. </template>
  130. <style lang="scss" scoped>
  131. .translate50 {
  132. transform: translateY(-50%);
  133. }
  134. .course-detail-page {
  135. @apply flex flex-row items-center;
  136. background-color: #3498DB;
  137. height: 100vh;
  138. color: black;
  139. transition: all 1s ease-in-out;
  140. }
  141. .video-container {
  142. width: 100vw;
  143. height: 100vh;
  144. top: 0;
  145. left: 0;
  146. transform: translate(0, 0);
  147. animation: spin 2s linear 1;
  148. transition: all 2s ease-in-out;
  149. }
  150. @keyframes spin {
  151. 0% {
  152. opacity: 50%;
  153. }
  154. 100% {
  155. opacity: 1;
  156. }
  157. }
  158. .hidden {
  159. opacity: 0;
  160. transition: all 3s ease-in-out;
  161. }
  162. .video-fullscreen_title {
  163. @apply absolute top-3 left-0 pl-[80px] w-[100vw] z-10 flex items-center justify-between flex-row;
  164. color: #fff;
  165. .control-progress {
  166. @apply flex flex-row gap-[1vh] relative justify-end;
  167. flex: 1;
  168. &:before {
  169. content: '';
  170. @apply absolute top-1/2 right-0 w-[410px] h-[40px] bg-[#2BA0F3] rounded-l-full;
  171. transform: translateY(-30%);
  172. }
  173. }
  174. }
  175. video::-webkit-media-controls-fullscreen-button,
  176. video::-webkit-media-controls-enter-fullscreen-button,
  177. video::-webkit-media-controls-rotate-button,
  178. video::-webkit-media-controls-seek-back-button,
  179. video::-webkit-media-controls-seek-forward-button {
  180. display: none !important;
  181. }
  182. </style>