index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. import {
  2. computed,
  3. defineComponent,
  4. onBeforeMount,
  5. onMounted,
  6. reactive,
  7. ref
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. import icon_back from './image/icon_back.svg';
  11. import icon_arrow from './image/icon_arrow.svg';
  12. import { Button, Image, Popover, Tab, Tabs, showToast } from 'vant';
  13. import {
  14. api_lessonCoursewareFavoriteRemove,
  15. api_lessonCoursewareFavoriteSave,
  16. api_lessonCoursewareFavoriteage,
  17. api_lessonCoursewarePage,
  18. api_lessonCoursewareDetail
  19. } from './api';
  20. import { NImage } from 'naive-ui';
  21. import { state } from '@/state';
  22. import TheFavorite from '@/components/the-favorite';
  23. import { useRouter } from 'vue-router';
  24. import TheBook from './component/book';
  25. import { postMessage } from '@/helpers/native-message';
  26. import CoursewareList from '@/custom-plugins/guide-page/courseware-list'
  27. import './jquery.min.1.7.js';
  28. import './turn.js';
  29. import MEmpty from '@/components/m-empty';
  30. export const BOOK_DATA = {
  31. grades: [
  32. { text: '全部年级', value: '' },
  33. { text: '一年级', value: 1 },
  34. { text: '二年级', value: 2 },
  35. { text: '三年级', value: 3 },
  36. { text: '四年级', value: 4 },
  37. { text: '五年级', value: 5 },
  38. { text: '六年级', value: 6 },
  39. { text: '七年级', value: 7 },
  40. { text: '八年级', value: 8 },
  41. { text: '九年级', value: 9 }
  42. ],
  43. bookTypes: {
  44. LAST: '上册',
  45. NEXT: '下册'
  46. } as { [key: string]: string }
  47. };
  48. export default defineComponent({
  49. name: 'courseware-list',
  50. setup() {
  51. const router = useRouter();
  52. const popoverShow = ref(false);
  53. // 返回
  54. const goback = () => {
  55. postMessage({ api: 'goBack' });
  56. };
  57. const forms = reactive({
  58. currentGradeNum: 0,
  59. page: 1,
  60. rows: 10,
  61. type: 'COURSEWARE'
  62. });
  63. const _actions = computed(() => {
  64. return BOOK_DATA.grades.map((item, index) => {
  65. return {
  66. ...item,
  67. color:
  68. forms.currentGradeNum === index ? 'var(--van-primary-color)' : '',
  69. className: forms.currentGradeNum === index ? 'fontBlod' : ''
  70. };
  71. });
  72. });
  73. const onSelect = (action: any, index: number) => {
  74. forms.currentGradeNum = index;
  75. handleChange();
  76. };
  77. const isShowGuide = ref(false)
  78. const data = reactive({
  79. list: [] as any[],
  80. loading: false,
  81. favoriteList: [] as any[],
  82. tab: 'all',
  83. details: [] as any[],
  84. bookData: {} as any,
  85. showBook: false,
  86. book: {} as DOMRect
  87. });
  88. const getList = async () => {
  89. data.loading = true;
  90. const res = await api_lessonCoursewarePage({
  91. ...forms,
  92. currentGradeNum: forms.currentGradeNum ? forms.currentGradeNum : ''
  93. });
  94. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  95. data.list = res.data.rows.map((item: any) => {
  96. const type = BOOK_DATA.bookTypes[item.bookType];
  97. item.name = `${item.name}(${type})`;
  98. item.load = false;
  99. item.key = Date.now() + item.id;
  100. return item;
  101. });
  102. }
  103. data.loading = false;
  104. isShowGuide.value = true
  105. };
  106. const getFavoriteList = async () => {
  107. data.loading = true;
  108. const res = await api_lessonCoursewareFavoriteage({
  109. clientType: 'STUDENT',
  110. userId: state.user?.data?.id,
  111. page: forms.page,
  112. rows: forms.rows,
  113. currentGradeNum: forms.currentGradeNum ? forms.currentGradeNum : ''
  114. });
  115. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  116. data.list = res.data.rows.map((item: any) => {
  117. const type = BOOK_DATA.bookTypes[item.bookType];
  118. item.name = `${item.name}(${type})`;
  119. item.load = false;
  120. item.key = Date.now() + item.id;
  121. return item;
  122. });
  123. }
  124. data.loading = false;
  125. };
  126. const getData = () => {
  127. data.list = [];
  128. if (data.tab === 'all') {
  129. getList();
  130. }
  131. if (data.tab === 'favorite') {
  132. getFavoriteList();
  133. }
  134. };
  135. const handleChange = () => {
  136. forms.page = 1;
  137. getData();
  138. };
  139. onMounted(() => {
  140. getData();
  141. });
  142. const handleFavorite = async (item: any) => {
  143. if (item.favoriteFlag) {
  144. await api_lessonCoursewareFavoriteSave({
  145. lessonCoursewareId: item.id
  146. });
  147. } else {
  148. await api_lessonCoursewareFavoriteRemove({
  149. lessonCoursewareId: item.id
  150. });
  151. }
  152. };
  153. let timer: any = null;
  154. const dubounce = (fn: any, delay: number = 300) => {
  155. if (timer) {
  156. clearTimeout(timer);
  157. }
  158. timer = setTimeout(fn, delay);
  159. };
  160. /** 学生端根据教材编号获取关联的单元、章节 */
  161. const getDetail = async (item: any) => {
  162. const res = await api_lessonCoursewareDetail(item.id);
  163. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  164. data.details = res.data.lessonList || [];
  165. data.bookData = res.data;
  166. console.log('🚀 ~ data.details:', data.details);
  167. }
  168. };
  169. const handleCreateContainer = (id: string) => {
  170. const box = document.querySelector(
  171. `[data-id="${id}"]`
  172. ) as unknown as HTMLElement;
  173. if (!box) return;
  174. const rect = box.getBoundingClientRect();
  175. data.book = rect;
  176. };
  177. const handleRender = (fn: any) => {
  178. requestAnimationFrame(() => {
  179. requestAnimationFrame(() => {
  180. fn();
  181. });
  182. });
  183. };
  184. const handleOpen = async (item: any) => {
  185. await getDetail(item);
  186. handleCreateContainer(item.id);
  187. handleRender(() => {
  188. data.showBook = true;
  189. });
  190. };
  191. return () => (
  192. <div class={styles.container}>
  193. <div class={styles.head} style={{ opacity: data.showBook ? 0 : '' }}>
  194. <div class={styles.back} onClick={goback}>
  195. <img src={icon_back} />
  196. </div>
  197. <Tabs
  198. class={styles.tabs}
  199. v-model:active={data.tab}
  200. onChange={() => handleChange()}>
  201. <Tab title="全部教材" name="all"></Tab>
  202. <Tab name="favorite" v-slots={{
  203. title:() => (<div id='courseware-2'>我的收藏</div>)
  204. }}></Tab>
  205. </Tabs>
  206. <Popover
  207. v-model:show={popoverShow.value}
  208. class={styles.popover}
  209. actions={_actions.value}
  210. onSelect={onSelect}>
  211. {{
  212. reference: () => (
  213. <Button class={styles.downBtn} round size="small" {...{id:'courseware-3'}} >
  214. {BOOK_DATA.grades[forms.currentGradeNum].text}{' '}
  215. {/* <img class={styles.icon} src={icon_arrow} /> */}
  216. <svg
  217. class={[
  218. styles.icon,
  219. popoverShow.value ? styles.iconUp : ''
  220. ]}
  221. width="9px"
  222. height="5px"
  223. viewBox="0 0 9 5"
  224. version="1.1"
  225. xmlns="http://www.w3.org/2000/svg">
  226. <title>三角形</title>
  227. <g
  228. id="演示用"
  229. stroke="none"
  230. stroke-width="1"
  231. fill="currentColor"
  232. fill-rule="evenodd">
  233. <g
  234. id="全部教材-筛选"
  235. transform="translate(-769.000000, -35.000000)"
  236. fill="currentColor">
  237. <g
  238. id="编组-3"
  239. transform="translate(696.000000, 20.000000)">
  240. <g
  241. id="筛选目录备份-2"
  242. transform="translate(13.000000, 7.000000)">
  243. <path
  244. 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"
  245. id="三角形"
  246. transform="translate(64.500000, 10.500000) rotate(-180.000000) translate(-64.500000, -10.500000) "></path>
  247. </g>
  248. </g>
  249. </g>
  250. </g>
  251. </svg>
  252. </Button>
  253. )
  254. }}
  255. </Popover>
  256. </div>
  257. <div class={styles.content}>
  258. <div
  259. class={[
  260. styles.wrap,
  261. data.list.length <= 0 && !data.loading ? styles.emtpyWrap : ''
  262. ]}>
  263. {data.list.map((item, index) => {
  264. return (
  265. <div
  266. class={[
  267. styles.wrapItem,
  268. data.bookData.id === item.id && data.showBook
  269. ? styles.wrapItemHide
  270. : ''
  271. ]}
  272. key={item.key}
  273. onClick={() => handleOpen(item)}>
  274. {/* courseware- */}
  275. {index==0?<NImage
  276. data-id={item.id}
  277. {...{id:'courseware-0'}}
  278. class={[styles.cover, item.load ? styles.loaded : '']}
  279. objectFit="cover"
  280. src={item.coverImg}
  281. onLoad={() => {
  282. item.load = true;
  283. }}
  284. onError={() => {
  285. item.load = true;
  286. }}
  287. />: <NImage
  288. data-id={item.id}
  289. class={[styles.cover, item.load ? styles.loaded : '']}
  290. objectFit="cover"
  291. src={item.coverImg}
  292. onLoad={() => {
  293. item.load = true;
  294. }}
  295. onError={() => {
  296. item.load = true;
  297. }}
  298. />}
  299. <div class={styles.name}>{item.name}</div>
  300. {index==0? <div
  301. id='courseware-1'
  302. class={styles.favoriteBtn}
  303. onClick={(e: Event) => {
  304. e.stopPropagation();
  305. if (data.tab !== 'all') return;
  306. item.favoriteFlag = !item.favoriteFlag;
  307. dubounce(() => handleFavorite(item));
  308. }}>
  309. <TheFavorite isFavorite={data.tab !== 'all' ? true : item.favoriteFlag} />
  310. </div>: <div
  311. class={styles.favoriteBtn}
  312. onClick={(e: Event) => {
  313. e.stopPropagation();
  314. if (data.tab !== 'all') return;
  315. item.favoriteFlag = !item.favoriteFlag;
  316. dubounce(() => handleFavorite(item));
  317. }}>
  318. <TheFavorite isFavorite={data.tab !== 'all' ? true : item.favoriteFlag} />
  319. </div>}
  320. </div>
  321. );
  322. })}
  323. {data.list.length <= 0 && !data.loading && (
  324. <MEmpty description="暂无数据" />
  325. )}
  326. </div>
  327. </div>
  328. <TheBook
  329. show={data.showBook}
  330. bookData={data.bookData}
  331. rect={data.book}
  332. onClose={() => {
  333. data.showBook = false;
  334. }}
  335. />
  336. {isShowGuide.value? <CoursewareList></CoursewareList>:null}
  337. </div>
  338. );
  339. }
  340. });