index.tsx 9.4 KB

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