index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import request from '@/helpers/request'
  2. import { state } from '@/state'
  3. import {
  4. Button,
  5. Cell,
  6. CellGroup,
  7. Empty,
  8. Grid,
  9. GridItem,
  10. Icon,
  11. Image,
  12. Loading,
  13. showConfirmDialog,
  14. showToast,
  15. Skeleton,
  16. SkeletonImage,
  17. Space
  18. } from 'vant'
  19. import { defineComponent, onMounted, reactive, onUnmounted, nextTick, Transition, TransitionGroup } from 'vue'
  20. import styles from './index.module.less'
  21. import { useRoute, useRouter } from 'vue-router'
  22. import {
  23. listenerMessage,
  24. postMessage,
  25. promisefiyPostMessage,
  26. removeListenerMessage
  27. } from '@/helpers/native-message'
  28. import iconLook from './image/look.svg'
  29. import iconCourse from './image/icon-course.png'
  30. import iconCourseLock from './image/icon-course-lock.png'
  31. import { browser } from '@/helpers/utils'
  32. import OEmpty from '@/components/o-empty'
  33. import { handleCheckVip } from '../hook/useFee'
  34. import iconList from './image/icon-list.png'
  35. import OSticky from '@/components/o-sticky'
  36. import OHeader from '@/components/o-header'
  37. import { useEventListener } from '@vant/use'
  38. import OLoading from '@/components/o-loading'
  39. export default defineComponent({
  40. name: 'courseList',
  41. setup() {
  42. const route = useRoute()
  43. const router = useRouter()
  44. const browserInfo = browser()
  45. // const catchList = store
  46. const data = reactive({
  47. titleOpacity: 0,
  48. loading: true,
  49. detail: {
  50. cover: '',
  51. name: '',
  52. des: ''
  53. },
  54. list: [] as any
  55. })
  56. /** 获取课件详情 */
  57. const getDetail = async () => {
  58. const res: any = await request.get(
  59. `${state.platformApi}/lessonCourseware/detail/${route.query.id}`
  60. )
  61. if (res?.data) {
  62. data.detail.cover = res.data.coverImg
  63. data.detail.name = res.data.name
  64. data.detail.des = res.data.lessonTargetDesc
  65. }
  66. }
  67. const getList = async () => {
  68. data.loading = true
  69. if (route.query.courseScheduleId) {
  70. try {
  71. const res: any = await request.post(
  72. state.platformApi + '/courseSchedule/getCoursewareDetail',
  73. {
  74. params: {
  75. courseScheduleId: route.query.courseScheduleId,
  76. coursewareId: route.query.id
  77. }
  78. }
  79. )
  80. if (Array.isArray(res?.data)) {
  81. data.list = res.data
  82. }
  83. } catch (error) {}
  84. } else {
  85. try {
  86. const res: any = await request.post(
  87. state.platformApi + '/courseSchedule/myCoursewareDetail/' + route.query.id
  88. )
  89. if (Array.isArray(res?.data)) {
  90. const _list = await checkCoursewareCache(res.data)
  91. data.list = browserInfo.isApp
  92. ? res.data.map((item: any) => {
  93. const _item = _list.find(
  94. (n: any) => n.lessonCoursewareDetailId == item.lessonCoursewareDetailId
  95. )
  96. const n = {
  97. ...item
  98. }
  99. if (_item) {
  100. n.hasCache = _item.hasCache
  101. }
  102. return n
  103. })
  104. : res.data
  105. }
  106. } catch (error) {}
  107. }
  108. data.loading = false
  109. }
  110. onMounted(() => {
  111. getDetail()
  112. getList()
  113. listenerMessage('downloadCoursewareToCache', getProgress)
  114. })
  115. onUnmounted(() => {
  116. removeListenerMessage('downloadCoursewareToCache', getProgress)
  117. })
  118. const handleClick = async (item: any) => {
  119. if (!item.knowledgePointList) {
  120. showConfirmDialog({
  121. message: '该课件暂无知识点'
  122. })
  123. return
  124. }
  125. if (route.query.code === 'select') {
  126. console.log('选择课时')
  127. setCoursewareDetail(item)
  128. return
  129. }
  130. if (!item.hasCache) {
  131. const hasVip = handleCheckVip()
  132. if (!hasVip) return
  133. // 下载中不提示
  134. if (item.downloadStatus == 1) {
  135. return
  136. }
  137. // 重新下载
  138. if (item.downloadStatus == 3) {
  139. downCatch(item)
  140. return
  141. }
  142. try {
  143. await showConfirmDialog({
  144. message: '当前课程没有缓存,是否缓存?'
  145. })
  146. } catch (error) {
  147. gotoPlay(item)
  148. return
  149. }
  150. downCatch(item)
  151. return
  152. }
  153. gotoPlay(item)
  154. }
  155. // 去课件播放
  156. const gotoPlay = (item: any) => {
  157. postMessage({
  158. api: 'openWebView',
  159. content: {
  160. url: `${location.origin}${location.pathname}#/coursewarePlay?id=${item.lessonCoursewareDetailId}&source=my-course`,
  161. orientation: 0,
  162. isHideTitle: true,
  163. statusBarTextColor: false,
  164. isOpenLight: true,
  165. showLoadingAnim: true
  166. }
  167. })
  168. }
  169. // 检查数据的缓存状态
  170. const checkCoursewareCache = (list: []): Promise<any[]> => {
  171. return new Promise((resolve) => {
  172. postMessage(
  173. {
  174. api: 'checkCoursewareCache',
  175. content: {
  176. data: list
  177. }
  178. },
  179. (res) => {
  180. if (res?.content?.data) {
  181. resolve(res.content.data)
  182. return
  183. }
  184. return []
  185. }
  186. )
  187. })
  188. }
  189. // 下载缓存
  190. const downCatch = async (item: any) => {
  191. if (browserInfo.isApp) {
  192. const res = await postMessage({
  193. api: 'downloadCoursewareToCache',
  194. content: {
  195. data: item
  196. }
  197. })
  198. return res
  199. }
  200. return true
  201. }
  202. // 下载缓存进度
  203. const getProgress = (res: any) => {
  204. // console.log('🚀 ~ res', res)
  205. if (res?.content?.lessonCoursewareDetailId) {
  206. const { lessonCoursewareDetailId, downloadStatus, progress } = res.content
  207. const course = data.list.find(
  208. (n: any) => n.lessonCoursewareDetailId == lessonCoursewareDetailId
  209. )
  210. if (course) {
  211. course.downloadStatus = downloadStatus
  212. course.progress = progress
  213. if (downloadStatus == 2) {
  214. course.hasCache = 1
  215. course.progress = 100
  216. }
  217. }
  218. }
  219. }
  220. // 绑定课时
  221. const setCoursewareDetail = async (item: any) => {
  222. try {
  223. const res: any = await request.post(
  224. state.platformApi + '/courseSchedule/setCoursewareDetail',
  225. {
  226. params: {
  227. courseScheduleId: route.query.courseScheduleId,
  228. coursewareDetailId: item.lessonCoursewareDetailId
  229. }
  230. }
  231. )
  232. if (res.code === 200) {
  233. postMessage({ api: 'back' })
  234. }
  235. } catch (error) {}
  236. }
  237. useEventListener('scroll', (e: Event) => {
  238. const height = window.scrollY || window.pageYOffset || document.documentElement.scrollTop
  239. data.titleOpacity = height > 100 ? 1 : height / 100
  240. })
  241. return () => (
  242. <div class={styles.courseList}>
  243. <OHeader
  244. border={false}
  245. background={`rgba(255,255,255, ${data.titleOpacity})`}
  246. color="rgba(124, 61, 18, 1)"
  247. title="教材详情"
  248. />
  249. <div class={styles.periodContent}>
  250. <div class={styles.cover}>
  251. <img
  252. src={data.detail.cover}
  253. onLoad={(e: Event) => {
  254. if (e.target) {
  255. ;(e.target as any).style.opacity = 1
  256. }
  257. }}
  258. />
  259. </div>
  260. <div>
  261. <div class={styles.contentTitle}>{data.detail.name}</div>
  262. <div class={styles.contentLabel}>教学目标:{data.detail.des}</div>
  263. </div>
  264. </div>
  265. <TransitionGroup name="van-fade">
  266. {!data.loading && (
  267. <>
  268. <div key="periodTitle" class={styles.periodTitle}>
  269. <img class={styles.pIcon} src={iconList} />
  270. <div class={styles.pTitle}>课程列表</div>
  271. <div class={styles.pNum}>共{data.list.length}课</div>
  272. </div>
  273. <div key="list" class={styles.periodList}>
  274. <CellGroup inset>
  275. {data.list.map((item: any) => {
  276. const isLock =
  277. item.lockFlag ||
  278. ((route.query.code == 'select' || state.platformType == 'STUDENT') &&
  279. !item.unlock)
  280. const isSelect = route.query.code === 'select'
  281. return (
  282. <Cell
  283. border
  284. center
  285. title={item.coursewareDetailName}
  286. label={!browserInfo.isStudent ? `已使用${item.useNum || 0}次` : ''}
  287. onClick={() => !isLock && handleClick(item)}
  288. >
  289. {{
  290. icon: () => (
  291. <div class={styles.periodItem}>
  292. <div class={styles.periodItemModel}>
  293. <img src={isLock ? iconCourseLock : iconCourse} />
  294. </div>
  295. </div>
  296. ),
  297. value: () => (
  298. <>
  299. {isSelect ? (
  300. <Button
  301. disabled={isLock}
  302. class={[styles.baseBtn, isLock ? styles.disable : styles.look]}
  303. >
  304. 选择
  305. </Button>
  306. ) : item.knowledgePointList ? (
  307. <>
  308. {item.hasCache ? (
  309. <Button
  310. disabled={isLock}
  311. class={[
  312. styles.baseBtn,
  313. isLock ? styles.disable : styles.look
  314. ]}
  315. >
  316. 查看
  317. </Button>
  318. ) : (
  319. <Button
  320. disabled={isLock}
  321. class={[
  322. styles.baseBtn,
  323. isLock ? styles.disable : styles.down,
  324. item.downloadStatus ? styles.downing : ''
  325. ]}
  326. >
  327. {item.downloadStatus === 1
  328. ? `下载中 ${item.progress || 0}%`
  329. : item.downloadStatus === 2
  330. ? '下载成功'
  331. : item.downloadStatus === 3
  332. ? '重新下载'
  333. : '下载'}
  334. </Button>
  335. )}
  336. </>
  337. ) : (
  338. ''
  339. )}
  340. </>
  341. )
  342. }}
  343. </Cell>
  344. )
  345. })}
  346. </CellGroup>
  347. </div>
  348. </>
  349. )}
  350. </TransitionGroup>
  351. {data.loading && <OLoading />}
  352. {!data.loading && !data.list.length && <OEmpty tips="暂无内容" />}
  353. </div>
  354. )
  355. }
  356. })