index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. <template>
  2. <view class="wrap">
  3. <u-navbar :autoBack="true" title="培训" bgColor="rgba(255,255,255,255)" :placeholder="true" titleStyle="font-weight:bold;color:#121212"></u-navbar>
  4. <view class="search">
  5. <view class="b-input">
  6. <input class="s-input" :value="keyword" @input="onInput" @confirm="onSearch" placeholder="搜索培训" />
  7. <view class="s-submit iconfont icon-search" @click="onSearch"></view>
  8. </view>
  9. </view>
  10. <view class="selecter">
  11. <scroll-view scroll-y class="left-aside" :style="{ height: categoryScrollHeight + 'px' }">
  12. <block v-for="(item, index) in categoryList" :key="index">
  13. <view class="b-item" :class="{ active: index === categoryIndex }" @click="onCategorySelect(index)">
  14. {{ item.name }}
  15. </view>
  16. </block>
  17. <!-- 加载中 -->
  18. <load-more :loadingType="loadingType" loadingText=" "></load-more>
  19. </scroll-view>
  20. <view class="right-aside">
  21. <view class="goods-state">
  22. <text class="b-amount">
  23. <text>共有</text>
  24. <text class="s-num">{{ totalCount }}</text>
  25. <text>个培训</text>
  26. </text>
  27. <view class="b-filter">
  28. <view class="b-item" v-for="(item, index) in filterList" :key="index" :class="{ 'f-active': index == filterIndex }" @click="onFilterSelect(index)">
  29. <view class="s-tit">{{ item.name }}</view>
  30. </view>
  31. </view>
  32. </view>
  33. <block v-if="typeList.length > 0">
  34. <scroll-view class="type-list" scroll-x scroll-with-animation :scroll-into-view="'type-' + typeScroll" v-if="typeList.length > 0">
  35. <view class="b-items">
  36. <view
  37. class="b-item"
  38. :id="'type-' + index"
  39. v-for="(item, index) in typeList"
  40. :key="index"
  41. :class="{ active: index === typeIndex }"
  42. @click="onTypeSelect(index, true)"
  43. >
  44. {{ item.name }}
  45. </view>
  46. </view>
  47. </scroll-view>
  48. <scroll-view
  49. class="group-list"
  50. scroll-y
  51. scroll-with-animation
  52. :upper-threshold="100"
  53. :lower-threshold="100"
  54. :scroll-top="scrollTop"
  55. :scroll-into-view="'group-' + groupScroll"
  56. @scroll="onScroll"
  57. :style="{ height: groupScrollHeight + 'px' }"
  58. >
  59. <block v-if="groupLoad">
  60. <view class="group-item" v-for="(group, index) in groupList" :id="'group-' + index" :key="index">
  61. <view class="group-title">{{ group.name }}</view>
  62. <view class="group-main">
  63. <view class="schedule-com" v-for="item in group.scheduleList" :key="item.id">
  64. <schedule-item :item="item" scene="index"></schedule-item>
  65. </view>
  66. <!-- 加载中 -->
  67. <load-more :loadingType="group.loadingType" :loadingText="group.loadingText"></load-more>
  68. </view>
  69. </view>
  70. </block>
  71. <!-- 加载中 -->
  72. <load-more v-else :loadingType="loadingType" :loadingText="loadingText"></load-more>
  73. </scroll-view>
  74. </block>
  75. <scroll-view
  76. v-else
  77. class="schedule-list"
  78. scroll-y
  79. scroll-with-animation
  80. :lower-threshold="100"
  81. @scrolltolower="onScrollToLower"
  82. :style="{ height: goodsScrollHeight + 'px' }"
  83. >
  84. <view class="schedule-com" v-for="(item, index) in scheduleList" :key="index">
  85. <schedule-item :item="item" scene="index"></schedule-item>
  86. </view>
  87. <!-- 加载中 -->
  88. <load-more :loadingType="loadingType" :loadingText="loadingText"></load-more>
  89. </scroll-view>
  90. </view>
  91. </view>
  92. </view>
  93. </template>
  94. <script>
  95. // import { getCategoryList, getScheduleList } from '@/service/api/schedule.js';
  96. import mixinsCommon from '@/mixins/common.js';
  97. let that;
  98. export default {
  99. mixins: [mixinsCommon],
  100. data() {
  101. return {
  102. keyword: '',
  103. scrollTop: 0,
  104. categoryIndex: 0,
  105. categoryList: [
  106. {
  107. id: 0,
  108. name: '所有培训',
  109. level: 1,
  110. children: []
  111. }
  112. ],
  113. categoryScrollHeight: 100,
  114. typeScroll: 0,
  115. typeIndex: 0,
  116. typeList: [],
  117. filterIndex: 0,
  118. filterList: [
  119. {
  120. name: '全部',
  121. value: 'all'
  122. },
  123. {
  124. name: '免费',
  125. value: 'free'
  126. },
  127. {
  128. name: 'VIP',
  129. value: 'vip'
  130. }
  131. ],
  132. totalCount: 0,
  133. groupLoad: false,
  134. groupScroll: 0,
  135. groupList: [],
  136. groupScrollHeight: 100,
  137. scheduleList: [],
  138. goodsScrollHeight: 100,
  139. page: 1,
  140. loadingType: -1,
  141. loadingText: ''
  142. };
  143. },
  144. onLoad() {
  145. that = this;
  146. this.initScrollHeight();
  147. this.loadingType = 1;
  148. this.$api.getCategoryList({}, function (res) {
  149. console.log(res, 6969);
  150. that.loadingType = -1;
  151. if (res.code == 1) {
  152. that.categoryList = [...that.categoryList, ...res.data];
  153. that.onCategorySelect(0);
  154. }
  155. });
  156. // getCategoryList().then(([err, res]) => {
  157. // this.loadingType = -1;
  158. // if (!err) {
  159. // this.categoryList = [...this.categoryList, ...res];
  160. // this.onCategorySelect(0);
  161. // }
  162. // });
  163. },
  164. methods: {
  165. redirect(type, data) {
  166. switch (type) {
  167. case 'audit':
  168. this.$app.routePage(null, '/pages/goods/audit');
  169. break;
  170. }
  171. },
  172. onInput(e) {
  173. console.log('onInput', e);
  174. this.keyword = e.detail.value;
  175. },
  176. onFilterSelect(index) {
  177. this.filterIndex = index;
  178. this.onCategorySelect(this.categoryIndex);
  179. },
  180. onSearch() {
  181. if (this.keyword) {
  182. this.onCategorySelect(0);
  183. }
  184. },
  185. // 选择大类
  186. onCategorySelect(index) {
  187. // 清除搜索
  188. if (this.keyword && index !== 0) {
  189. this.keyword = '';
  190. }
  191. this.loadingType = 1;
  192. this.totalCount = 0;
  193. this.categoryIndex = index;
  194. this.groupLoad = false;
  195. if (!this.keyword && this.categoryList[index].children && this.categoryList[index].children.length > 0) {
  196. this.typeList = this.categoryList[index].children;
  197. let groupList = [];
  198. this.typeList.forEach((item) => {
  199. groupList.push({
  200. id: item.id,
  201. name: item.name,
  202. scrollTop: -1,
  203. scheduleList: [],
  204. loadingType: 2,
  205. loadingText: '暂无数据'
  206. });
  207. });
  208. this.groupList = groupList;
  209. this.onTypeSelect(0, true);
  210. } else {
  211. this.typeList = [];
  212. this.loadScheduleList(true);
  213. }
  214. },
  215. // 选择小类
  216. onTypeSelect(index, scrole) {
  217. console.log('onTypeSelect', index);
  218. // 清除搜索
  219. if (this.keyword) {
  220. this.keyword = '';
  221. }
  222. this.typeIndex = index;
  223. this.typeScroll = index;
  224. this.loadGroupGoods(index, scrole); // 加载当前组
  225. this.loadPrevGoods(); // 加载上一组
  226. this.loadNextGoods(); // 加载下一组
  227. },
  228. // 监听右侧栏滚动
  229. onScroll(e) {
  230. // console.log('onScroll', e)
  231. // 待优化
  232. let scrollTop = e.detail.scrollTop;
  233. let groupIndex = 0;
  234. this.groupList.forEach((item, index) => {
  235. if (item.scrollTop <= scrollTop && item.scheduleList.length > 0) {
  236. groupIndex = index;
  237. }
  238. });
  239. if (this.typeIndex != groupIndex) {
  240. console.log('scroll', groupIndex, scrollTop);
  241. this.onTypeSelect(groupIndex); // 选中子分类
  242. }
  243. },
  244. // 右侧栏滚动到底
  245. onScrollToLower(e) {
  246. console.log('onScrollToLower', e);
  247. this.loadScheduleList();
  248. },
  249. // 向前加载数据
  250. loadPrevGoods() {
  251. if (this.typeIndex > 0) {
  252. this.loadGroupGoods(this.typeIndex - 1);
  253. }
  254. },
  255. // 向后加载数据
  256. loadNextGoods() {
  257. if (this.typeIndex < this.typeList.length - 1) {
  258. this.loadGroupGoods(this.typeIndex + 1);
  259. }
  260. },
  261. loadGroupGoods(index, scroll) {
  262. console.log('loadGroupGoods', index, scroll);
  263. if (this.groupList[index].scheduleList.length === 0) {
  264. this.groupList[index].loadingType = 1;
  265. if (this.groupLoad === false && this.loadingType === -1) {
  266. this.loadingType = 1;
  267. }
  268. console.log('groupList', this.groupList);
  269. const typeId = this.typeList[this.typeIndex].id;
  270. const filterGroup = this.filterList[this.filterIndex].value;
  271. this.$api.getScheduleList(
  272. {
  273. category_id: this.typeList[this.typeIndex].id,
  274. group: this.filterList[this.filterIndex].value,
  275. keyword: this.keyword,
  276. page: this.page,
  277. page_size: 10
  278. },
  279. function (res) {
  280. console.log('5555', res);
  281. that.groupList[index].loadingType = -1;
  282. if (that.groupLoad === false && that.loadingType === 1) {
  283. that.loadingType = -1;
  284. that.groupLoad = true;
  285. }
  286. console.log('groupLoad', that.groupLoad);
  287. if (res.code == 1) {
  288. that.totalCount += res.data.total;
  289. if (res.data.items.length > 0) {
  290. that.groupList[index].scheduleList = res.data.items;
  291. } else {
  292. that.groupList[index].loadingType = 2;
  293. that.groupList[index].loadingText = '暂无数据';
  294. }
  295. // 计算group的偏移
  296. setTimeout(() => {
  297. that.calcGroupTop();
  298. if (scroll) {
  299. that.groupScroll = index;
  300. }
  301. }, 100);
  302. } else {
  303. that.groupList[index].loadingType = 3;
  304. }
  305. }
  306. );
  307. // getScheduleList(typeId, filterGroup, this.keyword, 1, 100).then(([err, res]) => {
  308. // console.log('getScheduleList', err, res);
  309. // this.groupList[index].loadingType = -1;
  310. // if (this.groupLoad === false && this.loadingType === 1) {
  311. // this.loadingType = -1;
  312. // this.groupLoad = true;
  313. // }
  314. // console.log('groupLoad', this.groupLoad);
  315. // if (!err) {
  316. // this.totalCount += res.total;
  317. // if (res.items.length > 0) {
  318. // this.groupList[index].scheduleList = res.items;
  319. // } else {
  320. // this.groupList[index].loadingType = 2;
  321. // this.groupList[index].loadingText = '暂无数据';
  322. // }
  323. // // 计算group的偏移
  324. // setTimeout(() => {
  325. // this.calcGroupTop();
  326. // if (scroll) {
  327. // this.groupScroll = index;
  328. // }
  329. // }, 100);
  330. // } else {
  331. // this.groupList[index].loadingType = 3;
  332. // }
  333. // });
  334. } else {
  335. if (scroll) {
  336. this.groupScroll = index;
  337. }
  338. }
  339. },
  340. loadScheduleList(refresh) {
  341. console.log('loadScheduleList', refresh);
  342. if (refresh) {
  343. this.page = 1;
  344. this.scheduleList = [];
  345. } else {
  346. this.page++;
  347. }
  348. this.loadingType = 1;
  349. this.loadingText = '';
  350. const categoryId = this.categoryList[this.categoryIndex].id;
  351. const filterGroup = this.filterList[this.filterIndex].value;
  352. this.$api.getScheduleList(
  353. {
  354. category_id: this.categoryList[this.categoryIndex].id,
  355. group: this.filterList[this.filterIndex].value,
  356. keyword: this.keyword,
  357. page: this.page,
  358. page_size: 10
  359. },
  360. function (res) {
  361. console.log(res, 3636);
  362. that.loadingType = -1;
  363. if (res.code == 1) {
  364. that.totalCount = res.data.total;
  365. if (res.data.items.length > 0) {
  366. that.scheduleList = [...that.scheduleList, ...res.data.items];
  367. } else {
  368. that.loadingType = 2;
  369. if (that.page == 1) {
  370. that.loadingText = '暂无数据';
  371. }
  372. }
  373. } else {
  374. that.loadingType = 3;
  375. }
  376. }
  377. );
  378. // getScheduleList(categoryId, filterGroup, this.keyword, this.page, 10).then(([err, res]) => {
  379. // console.log('getScheduleList', err, res);
  380. // this.loadingType = -1;
  381. // if (!err) {
  382. // this.totalCount = res.total;
  383. // if (res.items.length > 0) {
  384. // this.scheduleList = [...this.scheduleList, ...res.items];
  385. // } else {
  386. // this.loadingType = 2;
  387. // if (this.page == 1) {
  388. // this.loadingText = '暂无数据';
  389. // }
  390. // }
  391. // } else {
  392. // this.loadingType = 3;
  393. // }
  394. // });
  395. },
  396. // 计算分组偏移
  397. calcGroupTop() {
  398. let height = 0;
  399. let calc = false;
  400. this.groupList.forEach((item, index) => {
  401. // if (calc === false && item.scheduleList.length === 0) {
  402. // calc = true
  403. // }
  404. // if (calc) {
  405. let groupView = uni
  406. .createSelectorQuery()
  407. .in(this)
  408. .select('#group-' + index);
  409. groupView
  410. .fields(
  411. {
  412. size: true
  413. },
  414. (res) => {
  415. console.log('Query fields', index, res);
  416. if (res) {
  417. item.scrollTop = height;
  418. height += res.height;
  419. }
  420. }
  421. )
  422. .exec();
  423. // } else {
  424. // height += item.scrollTop
  425. // }
  426. });
  427. this.sizeCalcState = true;
  428. },
  429. initScrollHeight() {
  430. const systemInfo = uni.getSystemInfoSync();
  431. const windowHeight = systemInfo.windowHeight;
  432. const searchBarHeight = this.$logic.rpx2px(90);
  433. const stateBarHeight = this.$logic.rpx2px(90);
  434. const typeListHeight = this.$logic.rpx2px(90);
  435. this.categoryScrollHeight = windowHeight - searchBarHeight;
  436. this.goodsScrollHeight = this.categoryScrollHeight - stateBarHeight;
  437. this.groupScrollHeight = this.goodsScrollHeight - typeListHeight;
  438. }
  439. }
  440. };
  441. </script>
  442. <style>
  443. .wrap {
  444. height: 100%;
  445. background-color: #f8f8f8;
  446. }
  447. .wrap {
  448. position: relative;
  449. }
  450. .search {
  451. margin: auto;
  452. padding: 0 30upx;
  453. width: 690upx;
  454. height: 90upx;
  455. background: #da5650;
  456. display: flex;
  457. align-items: center;
  458. }
  459. .search .b-input {
  460. padding: 0 20upx;
  461. flex: 1;
  462. height: 70upx;
  463. background: #fff;
  464. border-radius: 10upx;
  465. display: flex;
  466. align-items: center;
  467. }
  468. .search .b-input .s-input {
  469. flex: 1;
  470. color: #333;
  471. font-size: 26upx;
  472. }
  473. .search .b-input .s-submit {
  474. margin-left: 20upx;
  475. color: #c3c3c3;
  476. font-size: 40upx;
  477. }
  478. .selecter {
  479. display: flex;
  480. height: 100%;
  481. }
  482. .left-aside {
  483. padding-bottom: 100upx;
  484. flex-shrink: 0;
  485. width: 145rpx;
  486. height: 100%;
  487. background-color: #f1f2f1;
  488. }
  489. .left-aside .b-item {
  490. display: flex;
  491. align-items: center;
  492. justify-content: center;
  493. width: 100%;
  494. height: 90upx;
  495. font-size: 28upx;
  496. position: relative;
  497. }
  498. .left-aside .b-item.active {
  499. color: #da5650;
  500. background: #f8f8f8;
  501. }
  502. .left-aside .b-item.active:before {
  503. content: '';
  504. position: absolute;
  505. left: 0;
  506. top: 50%;
  507. transform: translateY(-50%);
  508. height: 36upx;
  509. width: 8upx;
  510. background-color: #da5650;
  511. border-radius: 0 4px 4px 0;
  512. opacity: 0.8;
  513. }
  514. .right-aside {
  515. flex: 1;
  516. }
  517. .right-aside .goods-state {
  518. padding: 0 20upx;
  519. height: 90upx;
  520. background: #f1f2f1;
  521. display: flex;
  522. align-items: center;
  523. justify-content: space-around;
  524. }
  525. .right-aside .goods-state .b-amount {
  526. color: #999;
  527. font-size: 26upx;
  528. }
  529. .right-aside .goods-state .b-amount .s-num {
  530. margin: 0 5upx;
  531. }
  532. .right-aside .goods-state .b-filter {
  533. display: flex;
  534. align-items: center;
  535. }
  536. .right-aside .goods-state .b-filter .b-item {
  537. padding: 0 20upx;
  538. display: flex;
  539. align-items: center;
  540. justify-content: center;
  541. }
  542. .right-aside .goods-state .b-filter .b-item.f-active {
  543. font-weight: bold;
  544. }
  545. .right-aside .goods-state .b-filter .b-item .s-tit {
  546. color: #666666;
  547. font-size: 26upx;
  548. }
  549. .right-aside .type-list {
  550. display: flex;
  551. width: 550upx;
  552. height: 90upx;
  553. background: #fff;
  554. white-space: nowrap;
  555. }
  556. .right-aside .type-list .b-items {
  557. padding: 20upx;
  558. }
  559. .right-aside .type-list .b-item {
  560. display: inline-block;
  561. margin-right: 20upx;
  562. padding: 0 20upx;
  563. height: 50upx;
  564. line-height: 50upx;
  565. background: #f8f8f8;
  566. border-radius: 10upx;
  567. color: rgba(0, 0, 0, 0.5);
  568. font-size: 24upx;
  569. }
  570. .right-aside .type-list .b-item:last-child {
  571. margin-right: 0;
  572. }
  573. .right-aside .type-list .b-item.active {
  574. color: #fff;
  575. background: #da5650;
  576. }
  577. .right-aside .group-list {
  578. height: 100%;
  579. background: #f8f8f8;
  580. }
  581. .right-aside .group-list .group-item {
  582. padding-top: 30upx;
  583. }
  584. .right-aside .group-list .group-title {
  585. padding: 0 20upx;
  586. margin-bottom: 10upx;
  587. color: #666;
  588. font-size: 28upx;
  589. font-weight: bold;
  590. }
  591. .right-aside .group-list .group-main .schedule-com {
  592. border-bottom: 1upx solid #eee;
  593. }
  594. .right-aside .group-list .group-main .schedule-com:last-child {
  595. border-bottom: none;
  596. }
  597. .right-aside .schedule-list .schedule-com {
  598. border-bottom: 1upx solid #eee;
  599. }
  600. .right-aside .schedule-list .schedule-com:last-child {
  601. border-bottom: none;
  602. }
  603. </style>