index.tsx 9.9 KB

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