detail.uvue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <script setup lang='ts'>
  2. import Back from '@/components/back.uvue'
  3. import Loading from '@/components/loading.uvue'
  4. import { ref, onMounted, watchEffect } 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 showVideo = ref(true)
  11. const data = ref({
  12. videoSrc: 'https://oss.xiaoxiongcode.com/course-1/animate/第1集_观察.mp4',
  13. webviewSrc: '',
  14. })
  15. const menuItems = [
  16. { id: 'watch', name: '看课', icon: 'https://oss.xiaoxiongcode.com/static/home/kanke.png' },
  17. { id: 'practice', name: '练习', icon: 'https://oss.xiaoxiongcode.com/static/home/lianxi.png' },
  18. { id: 'experiment', name: '虚拟实验', icon: 'https://oss.xiaoxiongcode.com/static/home/shiyan.png' },
  19. { id: 'diary', name: '科学日记', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' }
  20. ]
  21. const menu2Items = [
  22. { id: '1', name: '观察', icon: 'https://oss.xiaoxiongcode.com/static/home/kanke.png' },
  23. { id: '2', name: '提问', icon: 'https://oss.xiaoxiongcode.com/static/home/lianxi.png' },
  24. { id: '3', name: '假设', icon: 'https://oss.xiaoxiongcode.com/static/home/shiyan.png' },
  25. { id: '4', name: '实验', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  26. { id: '5', name: '总结', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  27. { id: '6', name: '拓展', icon: 'https://oss.xiaoxiongcode.com/static/home/riji.png' },
  28. ]
  29. //当前进度
  30. const progress = ref(1)
  31. const progress2 = ref(1)
  32. const recorderManager = ref<any>()
  33. const innerAudioContext = ref<any>()
  34. const voicePath = ref('')
  35. function initOnlibeRecord() {
  36. recorderManager.value = uni.getRecorderManager();
  37. innerAudioContext.value = uni.createInnerAudioContext();
  38. innerAudioContext.value.autoplay = true;
  39. recorderManager.value.onStop(function (res) {
  40. console.log('recorder stop' + JSON.stringify(res));
  41. voicePath.value = res.tempFilePath;
  42. });
  43. }
  44. const course = ref<SubjectCourseResult>()
  45. //通过路由参数获取课程id
  46. async function fetchCatalog() {
  47. course.value = await fetchSubjectCourseApp({ id: router.query().id })
  48. if (!course.value?.courseUserProgress) {
  49. await updateSubjectProgress({
  50. courseId: course.value?.id,
  51. mainProgress: 1,
  52. assistantProgress: 1,
  53. status: 0
  54. })
  55. progress.value = 1
  56. progress2.value = 1
  57. data.value.videoSrc = course.value?.detailItem?.observeVideoPath || ''
  58. data.value.webviewSrc = course.value?.detailItem?.observeAnimationPath || ''
  59. } else {
  60. progress.value = 1
  61. progress2.value = 1
  62. // progress.value = course.value?.courseUserProgress.mainProgress
  63. // progress2.value = course.value?.courseUserProgress.assistantProgress
  64. }
  65. }
  66. onMounted(async () => {
  67. await fetchCatalog()
  68. isLoading.value = false
  69. initOnlibeRecord()
  70. setTimeout(() => {
  71. showProgress.value = false
  72. isLoading.value = false
  73. }, 2000)
  74. })
  75. async function handleEnded() {
  76. // await updateSubjectProgress({
  77. // courseId: course.value?.id,
  78. // mainProgress: 1,
  79. // assistantProgress: 2,
  80. // status: 0
  81. // })
  82. // progress2.value = 2
  83. switch (progress2.value) {
  84. case 1:
  85. router.push({
  86. path: "/pages/catalog/web-view",
  87. query: {
  88. src: data.value.webviewSrc,
  89. progress: progress2.value,
  90. }
  91. });
  92. break
  93. case 2:
  94. case 4:
  95. case 5:
  96. case 6:
  97. progress2.value++
  98. break
  99. }
  100. }
  101. const status = ref('wait')
  102. onShow(() => {
  103. isLoading.value = true
  104. var pages = getCurrentPages();
  105. const prevPage = pages[pages.length - 1];
  106. console.log('prevPage', prevPage)
  107. status.value = prevPage?.status || 'wait'
  108. if (status.value === 'success') {
  109. progress2.value++
  110. prevPage.status = 'wait'
  111. }
  112. isLoading.value = false
  113. })
  114. function handleControlsToggle(e) {
  115. console.log(e)
  116. showControls.value = e.detail.show
  117. }
  118. watchEffect(() => {
  119. if (progress.value === 1) {
  120. switch (progress2.value) {
  121. case 1:
  122. data.value.videoSrc = course.value?.detailItem?.observeVideoPath
  123. data.value.webviewSrc = course.value?.detailItem?.observeAnimationPath
  124. break
  125. case 2:
  126. data.value.videoSrc = course.value?.detailItem?.questionVideoPath
  127. break
  128. case 3:
  129. // data.value.webviewSrc = course.value?.detailItem?.assumeAnimationPath || data.value.webviewSrc
  130. // router.push({
  131. // path: "/pages/catalog/web-view",
  132. // query: {
  133. // src: data.value.webviewSrc,
  134. // progress: progress2.value,
  135. // }
  136. // });
  137. break
  138. case 4:
  139. // data.value.videoSrc = course.value?.detailItem?.testVideoPath || data.value.videoSrc
  140. data.value.webviewSrc = course.value?.detailItem?.testAnimationPath
  141. router.push({
  142. path: "/pages/catalog/web-view",
  143. query: {
  144. src: data.value.webviewSrc,
  145. progress: progress2.value,
  146. }
  147. });
  148. break
  149. case 5:
  150. data.value.videoSrc = course.value?.detailItem?.summaryVideoPath
  151. break
  152. case 6:
  153. data.value.videoSrc = course.value?.detailItem?.expandVideoPath
  154. break
  155. }
  156. }
  157. })
  158. function handleTop() {
  159. progress2.value = 4
  160. console.log(progress2.value)
  161. }
  162. function startRecord() {
  163. console.log('开始录音');
  164. recorderManager.value.start();
  165. }
  166. function endRecord() {
  167. console.log('录音结束');
  168. recorderManager.value.stop();
  169. }
  170. function playVoice() {
  171. console.log('播放录音');
  172. if (voicePath.value) {
  173. innerAudioContext.value.src = voicePath.value;
  174. innerAudioContext.value.play();
  175. }
  176. }
  177. </script>
  178. <template>
  179. <Loading v-show="isLoading" />
  180. <cl-page v-show="!isLoading">
  181. <Back v-show="showProgress" />
  182. <view class="w-[64vw] h-[36vw] absolute top-1/2 z-[1] left-[7vw] translate50"
  183. :class="{ ' rounded-2xl p-[3px] bg-black mt-[25px] ': showProgress, 'video-container': !showProgress }">
  184. <video v-if="progress2 === 3" id="video1" class="w-full h-full " :class="{ 'rounded-2xl': showProgress }"
  185. :src="data.videoSrc" :show-center-play-btn="false" :show-background-playback-button="false"
  186. :show-fullscreen-btn="false" :show-casting-button="false" autoplay @controlstoggle="handleControlsToggle"
  187. @ended="handleEnded">
  188. </video>
  189. <view v-else class="w-full h-full ">
  190. <img src="https://oss.xiaoxiongcode.com/static/home/2.png" alt="" class="w-full h-full object-cover" />
  191. <view class="absolute top-1/2 left-1/2 text-[20px] font-bold text-white text-center">
  192. <button @tap="startRecord">开始录音</button>
  193. <button @tap="endRecord">停止录音</button>
  194. <button @tap="playVoice">播放录音</button>
  195. <button @tap="handleTop">下一步</button>
  196. </view>
  197. </view>
  198. <view class="video-fullscreen_title" v-show="showControls && !showProgress">
  199. <Back />
  200. <view class="text-[20px] font-bold text-white">
  201. {{ course?.mainTitle }}
  202. </view>
  203. <view class="control-progress">
  204. <view class="before"></view>
  205. <view v-for="(item, i) in menu2Items" :key="item.id"
  206. class="px-0 py-3 flex items-center justify-center gap-[5px] relative z-[1]"
  207. :class="{ '!bg-[#fff] rounded-full': progress2 == i + 1 }">
  208. <cl-image :src="item.icon" mode="heightFix" class="!h-[30px]"></cl-image>
  209. <text class="text-[14px] font-bold" :class="{ '!text-[#2BA0F3]': progress2 == i + 1 }">{{ item.name
  210. }}</text>
  211. </view>
  212. <view class="before"></view>
  213. </view>
  214. </view>
  215. </view>
  216. <view class="course-detail-page" :class="{ 'hidden': !showProgress }">
  217. <!-- 顶部标题栏 -->
  218. <view class="flex-[1] h-[100vh] relative">
  219. <view class="flex flex-row w-full pr-[3vw] pl-[10vw] items-center justify-between absolute top-[30px]">
  220. <text class="text-[5vh] font-bold">{{ course?.mainTitle }}</text>
  221. <text
  222. class="rounded-full p-[1vh] px-[2vh] border-2 border-[#fff] border-solid text-[#fff] text-[3vh] font-bold">课程收获</text>
  223. </view>
  224. </view>
  225. <!-- 右侧功能菜单 -->
  226. <view class="w-[25vw] h-[100vh] bg-[#5CBDFD] flex flex-col justify-center p-5">
  227. <view v-for="(item, i) in menuItems" :key="item.id" class="h-[20vh] flex flex-row items-center ">
  228. <view
  229. 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]"
  230. :class="{ '!bg-[#fff]': progress > i }">
  231. <cl-image :src="item.icon" mode="heightFix" class="!h-[7vh]"></cl-image>
  232. <text class="text-[2vh] font-bold">{{ item.name }}</text>
  233. </view>
  234. <view class="flex items-center h-[20vh] ml-[2vh] ">
  235. <view class="w-[1vh] bg-[#B4DBF7] flex-1" :class="{ '!bg-[#71C73D]': progress > i, '!opacity-0': i === 0 }">
  236. </view>
  237. <view class="w-[5vh] bg-[#fff] h-[5vh] rounded-full" :class="{ '!bg-[#71C73D]': progress > i }"></view>
  238. <view class="w-[1vh] bg-[#B4DBF7] flex-1"
  239. :class="{ '!bg-[#71C73D]': progress > i, '!opacity-0': i === menuItems.length - 1 }"></view>
  240. </view>
  241. </view>
  242. </view>
  243. </view>
  244. </cl-page>
  245. </template>
  246. <style lang="scss" scoped>
  247. .translate50 {
  248. transform: translateY(-50%);
  249. }
  250. .course-detail-page {
  251. @apply flex flex-row items-center;
  252. background-color: #3498DB;
  253. height: 100vh;
  254. color: black;
  255. transition: all 1s ease-in-out;
  256. }
  257. .video-container {
  258. width: 100vw;
  259. height: 100vh;
  260. top: 0;
  261. left: 0;
  262. transform: translate(0, 0);
  263. animation: spin 2s linear 1;
  264. transition: all 2s ease-in-out;
  265. }
  266. @keyframes spin {
  267. 0% {
  268. opacity: 50%;
  269. }
  270. 100% {
  271. opacity: 1;
  272. }
  273. }
  274. .hidden {
  275. opacity: 0;
  276. transition: all 3s ease-in-out;
  277. }
  278. .video-fullscreen_title {
  279. @apply absolute top-3 left-0 pl-[80px] w-[100vw] z-10 flex items-center justify-between flex-row;
  280. color: #fff;
  281. .control-progress {
  282. @apply flex flex-row gap-[1vh] relative justify-end;
  283. flex: 1;
  284. .before {
  285. @apply absolute top-1/2 right-0 w-[410px] h-[40px] bg-[#2BA0F3] rounded-l-full;
  286. transform: translateY(-30%);
  287. }
  288. }
  289. }
  290. video::-webkit-media-controls-fullscreen-button,
  291. video::-webkit-media-controls-enter-fullscreen-button,
  292. video::-webkit-media-controls-rotate-button,
  293. video::-webkit-media-controls-seek-back-button,
  294. video::-webkit-media-controls-seek-forward-button {
  295. display: none !important;
  296. }
  297. </style>