index.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. import {
  2. computed,
  3. defineComponent,
  4. nextTick,
  5. onBeforeMount,
  6. onMounted,
  7. reactive,
  8. ref
  9. } from 'vue';
  10. import styles from './index.module.less';
  11. import icon_back from './image/icon_back.svg';
  12. import icon_arrow from './image/icon_arrow.svg';
  13. import {
  14. Button,
  15. Image,
  16. Popover,
  17. Popup,
  18. Tab,
  19. Tabs,
  20. Tag,
  21. popoverProps,
  22. showConfirmDialog,
  23. showDialog,
  24. showToast
  25. } from 'vant';
  26. import {
  27. api_lessonCoursewareFavoriteRemove,
  28. api_lessonCoursewareFavoriteSave,
  29. api_lessonCoursewareFavoriteage,
  30. api_lessonCoursewarePage,
  31. api_lessonCoursewareDetail,
  32. api_classLessonCoursewarePage,
  33. api_classLessonCoursewareDetail,
  34. api_schoolDetail,
  35. api_subjectList,
  36. api_bookVersionPage
  37. } from './api';
  38. import { NImage } from 'naive-ui';
  39. import { state } from '@/state';
  40. import TheFavorite from '@/components/the-favorite';
  41. import { useRouter } from 'vue-router';
  42. import TheBook from './component/book';
  43. import { postMessage } from '@/helpers/native-message';
  44. import CoursewareList from '@/custom-plugins/guide-page/courseware-list';
  45. import './jquery.min.1.7.js';
  46. import './turn.js';
  47. import MEmpty from '@/components/m-empty';
  48. import deepClone from '@/helpers/deep-clone';
  49. import book from './component/book';
  50. import { browser } from '@/helpers/utils';
  51. import CoursewareReload from './courseware-reload';
  52. export const BOOK_DATA = {
  53. grades: [
  54. // { text: '全部年级', value: '' },
  55. { text: '一年级', value: 1 },
  56. { text: '二年级', value: 2 },
  57. { text: '三年级', value: 3 },
  58. { text: '四年级', value: 4 },
  59. { text: '五年级', value: 5 },
  60. { text: '六年级', value: 6 },
  61. { text: '七年级', value: 7 },
  62. { text: '八年级', value: 8 },
  63. { text: '九年级', value: 9 }
  64. ],
  65. bookTypes: {
  66. LAST: '上册',
  67. NEXT: '下册'
  68. } as { [key: string]: string }
  69. };
  70. export default defineComponent({
  71. name: 'courseware-list',
  72. setup() {
  73. const router = useRouter();
  74. const popoverShow = ref(false);
  75. const reloadStatus = ref(false);
  76. const baseBookVerionList = ref([] as any);
  77. const bookVersionList = ref([] as any);
  78. // 返回
  79. const goback = () => {
  80. postMessage({ api: 'goBack' });
  81. };
  82. const coursewareStorage = localStorage.getItem('courseware-list')
  83. ? JSON.parse(localStorage.getItem('courseware-list') as any)
  84. : {};
  85. const forms = reactive({
  86. currentGradeNum: coursewareStorage.currentGradeNum || null,
  87. bookVersionId: coursewareStorage.bookVersionId || (null as any),
  88. // instrumentId:
  89. // coursewareStorage.instrumentId ||
  90. // state.user.data?.instrumentId ||
  91. // (null as any),
  92. // subjectId:
  93. // coursewareStorage.subjectId ||
  94. // state.user.data?.subjectId ||
  95. // (null as any),
  96. page: 1,
  97. rows: 999,
  98. type: 'COURSEWARE'
  99. });
  100. // const _actions = computed(() => {
  101. // return BOOK_DATA.grades.map((item, index) => {
  102. // return {
  103. // ...item,
  104. // color:
  105. // forms.currentGradeNum === index ? 'var(--van-primary-color)' : '',
  106. // className: forms.currentGradeNum === index ? 'fontBlod' : ''
  107. // };
  108. // });
  109. // });
  110. // const onSelect = (action: any, index: number) => {
  111. // forms.currentGradeNum = index;
  112. // handleChange();
  113. // };
  114. const isShowGuide = ref(false);
  115. const data = reactive({
  116. list: [] as any[],
  117. loading: false,
  118. favoriteList: [] as any[],
  119. tab: 'all',
  120. details: [] as any[],
  121. bookData: {} as any,
  122. bookLessonId: '',
  123. subjectList: [] as any,
  124. instrumentList: [] as any, // 乐器列表
  125. showBook: false,
  126. book: {} as DOMRect
  127. });
  128. const getTanentList = async () => {
  129. try {
  130. // const schoolInfos = state.user.data.schoolInfos;
  131. // const schoolId =
  132. // schoolInfos && schoolInfos.length > 0 ? schoolInfos[0].id : null;
  133. // if (!schoolId) return;
  134. // await api_schoolDetail({
  135. // id: schoolId
  136. // });
  137. const { data } = await api_bookVersionPage({ type: 'COURSEWARE' });
  138. const result = data.rows || []
  139. baseBookVerionList.value = result.map((item: any) => {
  140. return {
  141. bookVersionId: item.id,
  142. bookVersionName: item.name
  143. };
  144. });
  145. bookVersionList.value = deepClone(baseBookVerionList.value);
  146. const index = baseBookVerionList.value.findIndex(
  147. (item: any) => item.bookVersionId == coursewareStorage.bookVersionId
  148. );
  149. // 判断列表中是存在,缓存的教材数据
  150. if (index < 0) {
  151. forms.bookVersionId = null;
  152. }
  153. } catch (e) {
  154. //
  155. console.log(e);
  156. }
  157. };
  158. const getList = async () => {
  159. data.loading = true;
  160. const { bookVersionId, currentGradeNum, ...more } = forms;
  161. const res = await api_lessonCoursewarePage({
  162. ...more,
  163. bookVersionId: bookVersionId == -1 ? null : bookVersionId,
  164. currentGradeNum: currentGradeNum ? currentGradeNum : ''
  165. });
  166. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  167. data.list = res.data.rows.map((item: any) => {
  168. item.load = false;
  169. item.key = Date.now() + item.id;
  170. return item;
  171. });
  172. }
  173. data.loading = false;
  174. setTimeout(() => {
  175. isShowGuide.value = true;
  176. }, 100);
  177. };
  178. const getFavoriteList = async () => {
  179. data.loading = true;
  180. const res = await api_lessonCoursewareFavoriteage({
  181. clientType: 'STUDENT',
  182. userId: state.user?.data?.id,
  183. page: forms.page,
  184. rows: forms.rows,
  185. // subjectId: forms.subjectId,
  186. bookVersionId: forms.bookVersionId != -1 ? forms.bookVersionId : '',
  187. currentGradeNum: forms.currentGradeNum ? forms.currentGradeNum : ''
  188. });
  189. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  190. data.list = res.data.rows.map((item: any) => {
  191. item.name = `${item.name}`;
  192. item.load = false;
  193. item.favoriteFlag = true;
  194. item.key = Date.now() + item.id;
  195. return item;
  196. });
  197. }
  198. data.loading = false;
  199. };
  200. const getLessonCourseware = async () => {
  201. data.loading = true;
  202. const res = await api_classLessonCoursewarePage({
  203. // clientType: 'STUDENT',
  204. // userId: state.user?.data?.id,
  205. bookVersionId: forms.bookVersionId ? forms.bookVersionId : '',
  206. page: forms.page,
  207. rows: forms.rows,
  208. // subjectId: forms.subjectId,
  209. currentGradeNum: forms.currentGradeNum ? forms.currentGradeNum : ''
  210. });
  211. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  212. data.list = res.data.rows.map((item: any) => {
  213. item.load = false;
  214. item.key = Date.now() + item.id;
  215. return item;
  216. });
  217. }
  218. data.loading = false;
  219. // isShowGuide.value = true;
  220. };
  221. const getData = () => {
  222. if (data.tab === 'all') {
  223. getList();
  224. }
  225. if (data.tab === 'favorite') {
  226. getFavoriteList();
  227. }
  228. if (data.tab === 'course') {
  229. getLessonCourseware();
  230. }
  231. };
  232. const contentContainerRef = ref();
  233. const handleChange = () => {
  234. if (data.tab === 'course') {
  235. bookVersionList.value = [
  236. ...deepClone(baseBookVerionList.value),
  237. {
  238. bookVersionId: -1,
  239. bookVersionName: '自定义'
  240. }
  241. ];
  242. } else {
  243. bookVersionList.value = deepClone(baseBookVerionList.value);
  244. }
  245. getData();
  246. if (contentContainerRef.value && browser().isTablet) {
  247. contentContainerRef.value.scrollTo(0, 0);
  248. }
  249. };
  250. const getSubjectList = async () => {
  251. try {
  252. const res = await api_subjectList({
  253. enableFlag: true,
  254. delFlag: 0,
  255. page: 1,
  256. rows: 999
  257. });
  258. // console.log(res.data, ' subjectList');
  259. data.subjectList = res.data.rows || [];
  260. } catch {}
  261. };
  262. onMounted(async () => {
  263. window.addEventListener('error', (e: any) => {
  264. console.log(e, 'error');
  265. });
  266. // console.log(browser().isTablet, 'browser().isTablet');
  267. await getTanentList();
  268. await getSubjectList();
  269. // if (forms.subjectId && data.subjectList.length > 0) {
  270. // data.subjectList.forEach((item: any) => {
  271. // if (item.id == forms.subjectId) {
  272. // data.instrumentList = item.instruments || [];
  273. // }
  274. // });
  275. // }
  276. getData();
  277. // 安卓的状态栏
  278. postMessage({
  279. api: 'setStatusBarVisibility',
  280. content: {
  281. isVisibility: 0
  282. }
  283. });
  284. });
  285. const handleFavorite = async (item: any) => {
  286. if (item.favoriteFlag) {
  287. await api_lessonCoursewareFavoriteSave({
  288. lessonCoursewareId: item.id
  289. });
  290. } else {
  291. await api_lessonCoursewareFavoriteRemove({
  292. lessonCoursewareId: item.id
  293. });
  294. if (data.tab === 'favorite') {
  295. // getData();
  296. // item.favoriteFlag = !item.favoriteFlag;
  297. }
  298. }
  299. };
  300. let timer: any = null;
  301. const dubounce = (fn: any, delay: number = 300) => {
  302. if (timer) {
  303. clearTimeout(timer);
  304. }
  305. timer = setTimeout(fn, delay);
  306. };
  307. /** 学生端根据教材编号获取关联的单元、章节 */
  308. const getDetail = async (item: any) => {
  309. if (data.tab === 'course') {
  310. const res = await api_classLessonCoursewareDetail({
  311. id: item.id
  312. // subjectId: forms.subjectId
  313. });
  314. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  315. data.details = res.data.lessonList || [];
  316. data.bookData = res.data;
  317. data.bookLessonId = item.id;
  318. console.log('🚀 ~ data.details course:', data.details);
  319. } else {
  320. // showConfirmDialog({
  321. // title: '提示',
  322. // message: '课程教材已更新,是否重新加载?'
  323. // })
  324. // .then(() => {
  325. // // on confirm
  326. // getData();
  327. // })
  328. // .catch(() => {
  329. // // on cancel
  330. // });
  331. reloadStatus.value = true;
  332. return;
  333. }
  334. } else {
  335. const res = await api_lessonCoursewareDetail({
  336. id: item.id
  337. // subjectId: forms.subjectId
  338. });
  339. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  340. data.details = res.data.lessonList || [];
  341. data.bookData = res.data;
  342. data.bookLessonId = item.id;
  343. console.log('🚀 ~ data.details:', data.details);
  344. }
  345. }
  346. handleCreateContainer(item.id);
  347. handleRender(() => {
  348. data.showBook = true;
  349. });
  350. };
  351. const handleCreateContainer = (id: string) => {
  352. const box = document.querySelector(
  353. `[data-id="${id}"]`
  354. ) as unknown as HTMLElement;
  355. if (!box) return;
  356. const rect = box.getBoundingClientRect();
  357. data.book = rect;
  358. };
  359. const handleRender = (fn: any) => {
  360. requestAnimationFrame(() => {
  361. requestAnimationFrame(() => {
  362. fn();
  363. });
  364. });
  365. };
  366. const handleOpen = async (item: any) => {
  367. await getDetail(item);
  368. };
  369. return () => (
  370. <div
  371. class={[
  372. styles.container,
  373. // styles.containerTablet,
  374. browser().isTablet ? styles.containerTablet : ''
  375. ]}>
  376. <div class={styles.head} style={{ opacity: data.showBook ? 0 : '' }}>
  377. <div class={styles.back} onClick={goback}>
  378. <img src={icon_back} />
  379. </div>
  380. <Tabs
  381. class={styles.tabs}
  382. v-model:active={data.tab}
  383. onChange={() => handleChange()}>
  384. <Tab title="全部教材" name="all"></Tab>
  385. <Tab title="课程教材" name="course"></Tab>
  386. <Tab
  387. name="favorite"
  388. v-slots={{
  389. title: () => <div id="courseware-2">我的收藏</div>
  390. }}></Tab>
  391. </Tabs>
  392. <Button
  393. class={[
  394. styles.downBtn,
  395. (data.tab != 'course' && forms.bookVersionId > 0) ||
  396. (data.tab == 'course' && forms.bookVersionId) ||
  397. forms.currentGradeNum
  398. ? styles.activeBtn
  399. : ''
  400. ]}
  401. round
  402. size="small"
  403. onClick={() => (popoverShow.value = true)}
  404. {...{ id: 'courseware-3' }}>
  405. 筛选
  406. <svg
  407. class={[styles.icon, popoverShow.value ? styles.iconUp : '']}
  408. width="9px"
  409. height="5px"
  410. viewBox="0 0 9 5"
  411. version="1.1"
  412. xmlns="http://www.w3.org/2000/svg">
  413. <title>三角形</title>
  414. <g
  415. id="演示用"
  416. stroke="none"
  417. stroke-width="1"
  418. fill="currentColor"
  419. fill-rule="evenodd">
  420. <g
  421. id="全部教材-筛选"
  422. transform="translate(-769.000000, -35.000000)"
  423. fill="currentColor">
  424. <g id="编组-3" transform="translate(696.000000, 20.000000)">
  425. <g
  426. id="筛选目录备份-2"
  427. transform="translate(13.000000, 7.000000)">
  428. <path
  429. d="M64.8716471,8.41294119 L68.2489659,12.1655176 C68.4336954,12.3707726 68.4170562,12.6869176 68.2118012,12.8716471 C68.1199888,12.9542782 68.0008397,13 67.8773188,13 L61.1226812,13 C60.8465388,13 60.6226812,12.7761424 60.6226812,12.5 C60.6226812,12.3764791 60.668403,12.25733 60.7510341,12.1655176 L64.1283529,8.41294119 C64.3130824,8.20768618 64.6292274,8.19104698 64.8344824,8.37577649 C64.8475136,8.38750459 64.859919,8.39990996 64.8716471,8.41294119 Z"
  430. id="三角形"
  431. transform="translate(64.500000, 10.500000) rotate(-180.000000) translate(-64.500000, -10.500000) "></path>
  432. </g>
  433. </g>
  434. </g>
  435. </g>
  436. </svg>
  437. </Button>
  438. </div>
  439. <div
  440. ref={contentContainerRef}
  441. class={[
  442. styles.content,
  443. data.list.length <= 0 && !data.loading ? styles.contentEmpty : ''
  444. ]}>
  445. <div
  446. class={[
  447. styles.wrap,
  448. data.list.length <= 0 && !data.loading ? styles.emtpyWrap : ''
  449. ]}>
  450. {data.list.map((item, index) => {
  451. return (
  452. <div
  453. class={[
  454. styles.wrapItem,
  455. data.bookData.id === item.id && data.showBook
  456. ? styles.wrapItemHide
  457. : ''
  458. ]}
  459. key={item.key}
  460. onClick={() => handleOpen(item)}>
  461. {/* courseware- */}
  462. <div class={styles.item}>
  463. <NImage
  464. data-id={item.id}
  465. {...{ id: index == 0 ? 'courseware-0' : '' }}
  466. class={[styles.cover, item.load ? styles.loaded : '']}
  467. objectFit="cover"
  468. src={item.coverImg}
  469. onLoad={() => {
  470. item.load = true;
  471. }}
  472. />
  473. </div>
  474. <div class={styles.name}>{item.name}</div>
  475. {/* 课程教材不需要收藏 */}
  476. {!data.loading && data.tab !== 'course' && (
  477. <div
  478. id={index === 0 ? 'courseware-1' : ''}
  479. class={styles.favoriteBtn}
  480. onClick={(e: Event) => {
  481. e.stopPropagation();
  482. item.favoriteFlag = !item.favoriteFlag;
  483. dubounce(() => handleFavorite(item));
  484. }}>
  485. <TheFavorite isFavorite={item.favoriteFlag} />
  486. </div>
  487. )}
  488. </div>
  489. );
  490. })}
  491. {data.list.length <= 0 && !data.loading && (
  492. <MEmpty image="list" description="暂无数据" />
  493. )}
  494. </div>
  495. </div>
  496. <TheBook
  497. show={data.showBook}
  498. bookData={data.bookData}
  499. bookLessonId={data.bookLessonId}
  500. // subjectId={forms.subjectId}
  501. tab={data.tab}
  502. rect={data.book}
  503. onClose={() => {
  504. data.showBook = false;
  505. }}
  506. onConfirm={() => {
  507. getData();
  508. }}
  509. />
  510. {isShowGuide.value ? <CoursewareList></CoursewareList> : null}
  511. <Popup v-model:show={popoverShow.value} class={styles.popupContainer}>
  512. <div class={styles.popoverContainer}>
  513. <div class={styles.searchList}>
  514. {bookVersionList.value.length > 0 && (
  515. <>
  516. <div class={styles.popoverTitle}>教材版本</div>
  517. <div class={[styles.popupList, styles.versionList]}>
  518. {bookVersionList.value.map((item: any) => (
  519. <Tag
  520. plain={forms.bookVersionId == item.bookVersionId}
  521. round
  522. onClick={() =>
  523. (forms.bookVersionId = item.bookVersionId)
  524. }>
  525. {item.bookVersionName}
  526. </Tag>
  527. ))}
  528. </div>
  529. </>
  530. )}
  531. <div class={styles.popoverTitle}>选择年级</div>
  532. <div class={[styles.popupList, styles.versionList]}>
  533. {BOOK_DATA.grades.map((item: any) => (
  534. <Tag
  535. plain={forms.currentGradeNum === item.value}
  536. round
  537. onClick={() => (forms.currentGradeNum = item.value)}>
  538. {item.text}
  539. </Tag>
  540. ))}
  541. </div>
  542. {/* <div class={styles.popoverTitle}>选择声部</div>
  543. <div class={[styles.popupList, styles.versionList]}>
  544. {data.subjectList.map((item: any) => (
  545. <span
  546. // plain={forms.subjectId == item.id}
  547. class={[
  548. styles.subjectItem,
  549. forms.subjectId == item.id && styles.active,
  550. item.instruments?.length > 0 && styles.arrow
  551. ]}
  552. onClick={() => {
  553. forms.subjectId = item.id;
  554. data.instrumentList = item.instruments || [];
  555. if (data.instrumentList.length > 0) {
  556. forms.instrumentId = data.instrumentList[0].id;
  557. }
  558. }}>
  559. {item.name}
  560. </span>
  561. ))}
  562. </div>
  563. {data.instrumentList.length > 0 && (
  564. <>
  565. <div class={styles.popoverTitle}>选择乐器</div>
  566. <div class={[styles.popupList, styles.versionList]}>
  567. {data.instrumentList.map((item: any) => (
  568. <Tag
  569. plain={forms.instrumentId == item.id}
  570. round
  571. onClick={() => (forms.instrumentId = item.id)}>
  572. {item.name}
  573. </Tag>
  574. ))}
  575. </div>
  576. </>
  577. )}*/}
  578. </div>
  579. <div class={styles.btnGroup}>
  580. <Button
  581. round
  582. onClick={() => {
  583. forms.bookVersionId = null;
  584. forms.currentGradeNum = null;
  585. // forms.subjectId = state.user.data?.subjectId || null;
  586. // forms.instrumentId = state.user.data?.instrumentId || null;
  587. // if (forms.subjectId && data.subjectList.length > 0) {
  588. // data.subjectList.forEach((item: any) => {
  589. // if (item.id == forms.subjectId) {
  590. // data.instrumentList = item.instruments || [];
  591. // }
  592. // });
  593. // }
  594. }}>
  595. 重置
  596. </Button>
  597. <Button
  598. round
  599. class={styles.btnSure}
  600. onClick={() => {
  601. // 保存缓存
  602. localStorage.setItem(
  603. 'courseware-list',
  604. JSON.stringify({
  605. bookVersionId: forms.bookVersionId,
  606. currentGradeNum: forms.currentGradeNum
  607. // subjectId: forms.subjectId,
  608. // instrumentId: forms.instrumentId
  609. })
  610. );
  611. getData();
  612. popoverShow.value = false;
  613. }}>
  614. 确认
  615. </Button>
  616. </div>
  617. </div>
  618. </Popup>
  619. {reloadStatus.value && (
  620. <CoursewareReload
  621. onClose={() => {
  622. reloadStatus.value = false;
  623. }}
  624. onConfirm={() => {
  625. getData();
  626. }}></CoursewareReload>
  627. )}
  628. </div>
  629. );
  630. }
  631. });