index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import {
  2. PropType,
  3. defineComponent,
  4. nextTick,
  5. onMounted,
  6. reactive,
  7. watch,
  8. ref
  9. } from 'vue';
  10. import styles from './index.module.less';
  11. import icon_back from '../../image/icon_back.svg';
  12. import icon_play from '../../image/icon_class.png';
  13. import { useRouter } from 'vue-router';
  14. import { listenerMessage, postMessage } from '@/helpers/native-message';
  15. import { showToast } from 'vant';
  16. import queryString from 'query-string';
  17. import CoursewareDetail from '@/custom-plugins/guide-page/courseware-detail';
  18. import { usePageVisibility } from '@vant/use';
  19. import { state } from '@/state';
  20. import TheNoticeBar from '@/components/the-noticeBar';
  21. export default defineComponent({
  22. name: 'the-book',
  23. props: {
  24. bookData: {
  25. type: Object as PropType<any>,
  26. default: () => ({})
  27. },
  28. tab: {
  29. type: String,
  30. default: ''
  31. },
  32. show: {
  33. type: Boolean,
  34. default: false
  35. },
  36. rect: {
  37. type: Object as PropType<DOMRect>,
  38. default: () => ({})
  39. }
  40. },
  41. emits: ['close'],
  42. setup(props, { emit }) {
  43. const router = useRouter();
  44. console.log(state.user.data.phone);
  45. const lastTimeKey = 'lastTime' + (state?.user?.data?.phone ?? '');
  46. const data = reactive({
  47. show: false,
  48. width: 0,
  49. height: 0,
  50. transform: '',
  51. list: [] as any[][],
  52. lastTime: localStorage.getItem(lastTimeKey)
  53. });
  54. const showGuide = ref(false);
  55. const isend = ref(false)
  56. const isHandMove = ref(false)
  57. const handleCreate = (key: string, url: string) => {
  58. return new Promise((resolve, reject) => {
  59. const _s = document.head.querySelector(`script[data-key="${key}"]`);
  60. if (!_s) {
  61. const s = document.createElement('script');
  62. s.setAttribute('data-key', key);
  63. s.src = url;
  64. s.onload = async () => {
  65. console.log(key + ' 加载完成');
  66. resolve(1);
  67. };
  68. document.head.appendChild(s);
  69. }
  70. });
  71. };
  72. const init = async () => {
  73. // await handleCreate('jquery', '/book/jquery.min.1.7.js');
  74. // await handleCreate('turn', '/book/turn.js');
  75. console.log('初始化完成');
  76. };
  77. let book: any = null;
  78. let timer: any = null;
  79. const handleBook = () => {
  80. book = (window as any).$('#flipbook');
  81. const height = document.body.clientHeight * 0.8;
  82. data.height = height;
  83. data.width = height * (210 / 297) * 2;
  84. book.turn({
  85. autoCenter: true,
  86. duration: 1000,
  87. disable: false,
  88. acceleration: true, // 是否启动硬件加速 如果为触摸设备必须为true
  89. // pages: 11, // 页码总数
  90. elevation: 50, // 转换期间页面的高度
  91. width: data.width, // 宽度 单位 px
  92. height: data.height, // 高度 单位 px
  93. gradients: true, // 是否显示翻页阴影效果
  94. // display: 'single', //设置单页还是双页
  95. })
  96. // book.bind('last', (event: Event, pageObject: any, corner: any) => {
  97. // // console.log(event, 'last')
  98. // noanimateClose()
  99. // });
  100. book.bind('turned', (event: Event, page: any, corner: any) => {
  101. setTimeout(() => {
  102. showGuide.value = true;
  103. }, 500);
  104. // if (page + 1 === book.turn('pages')) {
  105. // // noanimateClose()
  106. // handleClose()
  107. // // nextTick(() => {
  108. // // });
  109. // }
  110. });
  111. book.bind('turning', (event: Event, page: any, corner: any) => {
  112. console.log(page, 'page', book.turn('pages'))
  113. if (page === book.turn('pages')) {
  114. handleClose(false)
  115. // handleClose()
  116. // nextTick(() => {
  117. // });
  118. }
  119. return
  120. })
  121. // book.bind('turned', (e: any, page: any) => {
  122. };
  123. const getRect = () => {
  124. const bookWrap = document.querySelector(
  125. '.bookWrap'
  126. ) as unknown as HTMLElement;
  127. if (bookWrap) {
  128. const rect = bookWrap.getBoundingClientRect();
  129. const xScale = props.rect.width / (rect.width / 2);
  130. const yScale = props.rect.height / rect.height;
  131. const left =
  132. (((rect.width / 2) * (xScale - 1)) / 2 +
  133. props.rect.x -
  134. rect.x -
  135. rect.width / 4) /
  136. xScale;
  137. const top =
  138. ((rect.height * (yScale - 1)) / 2 + props.rect.y - rect.y) / yScale;
  139. const transform = `scale3d(${xScale}, ${yScale}, 1) translate(${left}px, ${top}px)`;
  140. bookWrap.style.transform = data.transform = transform;
  141. bookWrap.style.transition = 'transform 0s';
  142. nextTick(() => {
  143. requestAnimationFrame(() => {
  144. requestAnimationFrame(() => {
  145. bookWrap.style.transition = 'transform 1s';
  146. bookWrap.style.transform = '';
  147. data.show = true;
  148. timer = setTimeout(() => {
  149. book.turn('page', 2);
  150. setTimeout(() => {
  151. showGuide.value = true;
  152. }, 1500);
  153. }, 500);
  154. });
  155. });
  156. });
  157. }
  158. };
  159. const handleClose = (gotoOne = true) => {
  160. if (isend.value) {
  161. return
  162. }
  163. isend.value = true
  164. clearTimeout(timer);
  165. const bookWrap = document.querySelector(
  166. '.bookWrap'
  167. ) as unknown as HTMLElement;
  168. if (gotoOne) {
  169. book.turn('page', 1);
  170. }
  171. if (bookWrap) {
  172. bookWrap.style.transform = data.transform;
  173. }
  174. emit('close');
  175. setTimeout(() => {
  176. bookWrap.style.transition = '';
  177. bookWrap.style.transform = '';
  178. data.show = false;
  179. data.list = [];
  180. isend.value = false
  181. }, 1000);
  182. };
  183. onMounted(async () => {
  184. await init();
  185. listenerMessage('webViewOnResume', () => {
  186. data.lastTime = localStorage.getItem(lastTimeKey);
  187. });
  188. });
  189. const getList = () => {
  190. if (!props.bookData?.lessonList) return;
  191. const step = Math.floor((document.body.clientHeight * 0.8 - 40) / 50);
  192. console.log('🚀 ~ step:', step);
  193. const list = [];
  194. let listItem = [] as any[];
  195. for (let i = 0; i < props.bookData.lessonList.length; i++) {
  196. const item = props.bookData.lessonList[i];
  197. if (listItem.length >= step) {
  198. list.push([...listItem]);
  199. listItem = [{ name: item.name }];
  200. } else {
  201. listItem.push({ name: item.name });
  202. }
  203. for (let j = 0; j < item.knowledgeList.length; j++) {
  204. if (listItem.length >= step) {
  205. list.push([...listItem]);
  206. listItem = [item.knowledgeList[j]];
  207. } else {
  208. listItem.push(item.knowledgeList[j]);
  209. }
  210. }
  211. }
  212. if (listItem.length) {
  213. list.push(listItem);
  214. }
  215. data.list = list;
  216. // console.log('🚀 ~ data.list:', data.list.length);
  217. // console.log(book.turn.pages, 'book.turn.pages')
  218. // console.log(book.turn('pages'), 'pages')
  219. };
  220. watch(
  221. () => props.show,
  222. () => {
  223. if (props.show) {
  224. getList();
  225. nextTick(() => {
  226. handleBook();
  227. requestAnimationFrame(() => {
  228. getRect();
  229. });
  230. });
  231. }
  232. }
  233. );
  234. const handleOpenPlay = (item: any) => {
  235. if (item.id) {
  236. if (!item.containMaterial) {
  237. showToast('暂无资源');
  238. return;
  239. }
  240. localStorage.setItem(lastTimeKey, item.id);
  241. const query = queryString.stringify({
  242. id: item.id,
  243. lessonCoursewareId: item.lessonCoursewareId,
  244. lessonCoursewareDetailId: item.lessonCoursewareDetailId,
  245. name: item.name,
  246. tab: props.tab // 当前切换的是哪个类型
  247. });
  248. const url =
  249. location.origin + location.pathname + '#/courseware-play?' + query;
  250. console.log('🚀 ~ url:', url);
  251. postMessage({
  252. api: 'openWebView',
  253. content: {
  254. url,
  255. orientation: 0,
  256. isHideTitle: false,
  257. c_orientation: 0 // 0 横屏 1 竖屏
  258. }
  259. });
  260. // router.push({
  261. // path: '/courseware-play',
  262. // query: {
  263. // id: item.id,
  264. // lessonCoursewareId: item.lessonCoursewareId,
  265. // lessonCoursewareDetailId: item.lessonCoursewareDetailId,
  266. // name: item.name,
  267. // tab: props.tab
  268. // }
  269. // });
  270. }
  271. };
  272. const isStartAnimate = (item: any) => {
  273. // console.log(item) item.name.length > 9 ? true :
  274. return false
  275. }
  276. return () => (
  277. <div class={[styles.book, data.show ? '' : styles.bookHide]} onClick={() => handleClose()}>
  278. <div class={styles.back} >
  279. <img src={icon_back} />
  280. </div>
  281. <div class="bookWrap" style={{ width: data.width + 'px' }} onClick={(e: Event) => {
  282. e.stopPropagation()
  283. }} >
  284. {!!data.list.length && (
  285. <div id="flipbook" class={[data.show && 'animated']}>
  286. <div class="page">
  287. <img
  288. style="width: 100%; height: 100%; object-fit: cover;"
  289. src={props.bookData.coverImg}
  290. />
  291. </div>
  292. {data.list.map((list: any) => {
  293. return (
  294. <div class="page">
  295. <div class={styles.wrap}>
  296. <div class={styles.wrapItem}>
  297. {list.map((item: any, index: number) => {
  298. return (
  299. <>
  300. <div
  301. class={[styles.item, item.id && styles.des]}
  302. onTouchstart={(e: TouchEvent) => {
  303. e.stopPropagation();
  304. }}
  305. onClick={(e: Event) => {
  306. e.stopPropagation();
  307. handleOpenPlay(item);
  308. }}
  309. onTouchend={(e: TouchEvent) => {
  310. console.log(e)
  311. }}
  312. >
  313. {item.id ? (
  314. <img
  315. id={index == 1 ? 'coursewareDetail-0' : ''}
  316. class={styles.icon}
  317. src={icon_play}
  318. />
  319. ) : null}
  320. <div
  321. class={styles.name}
  322. style={{ lineHeight: '20Px' }}>
  323. {item.name}
  324. {/* <TheNoticeBar text={item.name} isAnimation={isStartAnimate(item)}></TheNoticeBar> */}
  325. {data.lastTime === item.id && (
  326. <span class={styles.last}>上次观看</span>
  327. )}
  328. </div>
  329. </div >
  330. </>
  331. );
  332. })}
  333. </div>
  334. </div>
  335. </div>
  336. );
  337. })}
  338. {data.list.length % 2 === 1 && (
  339. <div class="page" style={{ pointerEvents: 'none' }}>
  340. <div class={styles.wrap}>
  341. <div class={styles.wrapItem}></div>
  342. </div>
  343. </div>
  344. )}
  345. <div class="page">
  346. <img
  347. style="width: 100%; height: 100%; object-fit: cover;"
  348. src={props.bookData.coverImg}
  349. />
  350. {/* <div class={styles.wrap}>
  351. <div
  352. class={styles.wrapItem}
  353. style={{ backgroundColor: '#fff' }}></div>
  354. </div> */}
  355. </div>
  356. </div >
  357. )}
  358. </div >
  359. {showGuide.value ? <CoursewareDetail></CoursewareDetail> : null}
  360. </div >
  361. );
  362. }
  363. });