list.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <template>
  2. <div
  3. :class="mobile ? 'mobileMain' : ''"
  4. :style="{marginTop: mainMarginTop, marginBottom: mobile ? '0px' : '30px !important'}">
  5. <div class="skill-wrapper" v-if="!mobile">
  6. <div class="skill-top">
  7. <div class="tabs">
  8. <div class="tabs-item active">技能培训</div>
  9. <a href="/consult/" class="tabs-item">咨询服务</a>
  10. </div>
  11. <div class="skill-category-wrapper">
  12. <!-- <div class="category-title">技术分类</div> -->
  13. <!-- 一级分类内容 -->
  14. <div
  15. class="category-one-wrapper"
  16. :class="categoryExpanded ? 'expand' : ''">
  17. <!-- 更多 -->
  18. <!-- <div
  19. class="category-more"
  20. @click="handleClickExpandCategory">{{ categoryExpanded ? '收起' : '更多' }}</div> -->
  21. <!-- 全部 -->
  22. <a
  23. href="/learn/"
  24. class="category-one-item"
  25. :class="pagination.selectedCateIdOne == '' ? 'active' : ''">全部</a>
  26. <!-- 一级分类 -->
  27. <a
  28. v-for="categoryOne in skillCate"
  29. :key="categoryOne.value"
  30. class="category-one-item"
  31. :class="pagination.selectedCateIdOne == categoryOne.value ? 'active' : ''"
  32. :href="`/learn/${categoryOne.value}/`">{{ categoryOne.label }}</a>
  33. </div>
  34. <!-- 二级分类内容 -->
  35. <div class="category-two-wrapper">
  36. <div
  37. class="category-two-content"
  38. v-for="categoryOne in skillCate"
  39. :key="`cate-two-parnet${categoryOne.value}`"
  40. v-show="pagination.selectedCateIdOne == categoryOne.value">
  41. <a
  42. class="category-two-item"
  43. :class="pagination.selectedCateIdTwo == categoryTwo.value ? 'active' : ''"
  44. :href="`/learn/${categoryTwo.value}/`"
  45. v-for="categoryTwo in categoryOne.children"
  46. :key="`cate-two-${categoryTwo.value}`">{{ categoryTwo.label }}</a>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. <div class="skill-content">
  52. <div class="skill-list-wrapper" v-if="skillList.length">
  53. <div
  54. class="skill-item"
  55. v-for="item in skillList"
  56. :key="item.sale_id">
  57. <a :href="`/l/${item.sale_id}`">
  58. <img class="cover" :src="item.coverImage" alt="skillCover,cover">
  59. </a>
  60. <a class="owner-wrapper" :href="`/wo/${item.user.uid}/skill`">
  61. <img class="avatar" :src="item.user.icon_url" alt="avatar">
  62. <div class="nickname">{{ item.user.nickname }}</div>
  63. </a>
  64. <a class="title" :href="`/l/${item.sale_id}`">{{ item.title }}</a>
  65. <div class="price-wrapper">
  66. <div class="price-text">¥{{ item.price }}</div>
  67. <div v-if="item.buy_num>0" class="buy-num">{{ item.buy_num }}人已学习</div>
  68. </div>
  69. </div>
  70. </div>
  71. <div class="result-empty-wrapper" v-else>
  72. <img src="@/assets/img/common/empty@2x.png" alt="empty">
  73. <span>暂无搜索内容</span>
  74. </div>
  75. <div class="pagination-wrapper" v-if="pagination.total > pagination.pagesize">
  76. <el-pagination
  77. background
  78. layout="prev, pager, next"
  79. :current-page="pagination.page"
  80. :total="pagination.total"
  81. :page-size="pagination.pagesize"
  82. @current-change="handlePageChange">
  83. </el-pagination>
  84. </div>
  85. </div>
  86. </div>
  87. <div class="skill-wrapper-mobile" v-else>
  88. <div class="skill-category">
  89. <div class="skill-category-one">
  90. <div class="category-scroller">
  91. <div
  92. class="skill-category-one-item"
  93. :class="!pagination.selectedCateIdOne ? 'active' : ''"
  94. @click="handleClickCategoryOne(0)">
  95. 全部
  96. </div>
  97. <div
  98. class="skill-category-one-item"
  99. :class="pagination.selectedCateIdOne == category.value ? 'active' : ''"
  100. v-for="(category) in skillCate"
  101. :key="`category-one-${category.value}`"
  102. @click="handleClickCategoryOne(category.value)">
  103. {{ category.label }}
  104. </div>
  105. </div>
  106. <!-- filter -->
  107. <div class="filter-bg"></div>
  108. <div class="filter-wrapper" @click="handleShowCategoryDrawer">
  109. <img src="@/assets/img/works/filter-icon@2x.png" alt="filter">
  110. </div>
  111. </div>
  112. <div class="skill-category-two">
  113. <!-- 全部二级分类 -->
  114. <div
  115. class="skill-category-two-wrapper"
  116. v-show="!pagination.selectedCateIdOne">
  117. <div
  118. class="skill-category-two-item"
  119. :class="pagination.selectedCateIdTwo == category.value ? 'active' : ''"
  120. v-for="category in skillCateAll"
  121. :key="`category-all-${category.value}`"
  122. @click="handleClickCategoryTwo(category.value)">
  123. {{ category.label }}
  124. </div>
  125. </div>
  126. <!-- 接口返回的二级分类 -->
  127. <div
  128. class="skill-category-two-wrapper"
  129. v-for="(category) in skillCate"
  130. :key="`category-two-wrapper-${category.value}`"
  131. v-show="pagination.selectedCateIdOne === category.value">
  132. <div
  133. class="skill-category-two-item"
  134. :class="pagination.selectedCateIdTwo == categoryChild.value ? 'active' : ''"
  135. v-for="categoryChild in category.children"
  136. :key="`category-two-${categoryChild.value}`"
  137. @click="handleClickCategoryTwo(categoryChild.value)">
  138. {{ categoryChild.label }}
  139. </div>
  140. </div>
  141. </div>
  142. </div>
  143. <div class="skill-list" :class="showWxHeader ? 'skill-list__showWxHeader' : ''">
  144. <ul
  145. class="skill-list-wrapper"
  146. v-infinite-scroll="handleLoadMoreSkill"
  147. :infinite-scroll-disabled="pagination.noMore"
  148. :infinite-scroll-immediate="false">
  149. <div
  150. class="skill-item"
  151. v-for="item in skillList"
  152. :key="item.sale_id"
  153. @click="handleClickSkillItem(item.sale_id)">
  154. <img class="cover" :src="item.coverImage" alt="skillCover,cover">
  155. <div class="owner-wrapper">
  156. <img class="avatar" :src="item.user.icon_url" alt="avatar">
  157. <div class="nickname">{{ item.user.nickname }}</div>
  158. </div>
  159. <div class="title">{{ item.title }}</div>
  160. <div class="price-wrapper">
  161. <div class="price-text">¥{{ item.price }}</div>
  162. <div class="right-info">{{ item.buy_num }}人已学习</div>
  163. </div>
  164. </div>
  165. <!-- 空数据 -->
  166. <div class="result-empty-wrapper" v-if="!skillList.length && !pagination.loading">
  167. <img src="@/assets/img/common/empty@2x.png" alt="empty">
  168. <span>暂无搜索内容</span>
  169. </div>
  170. <p v-if="pagination.loading" class="skill-list-tips">加载中...</p>
  171. <p v-if="skillList.length && pagination.noMore" class="skill-list-tips">没有更多了</p>
  172. </ul>
  173. </div>
  174. <!-- 弹出的分类选择 -->
  175. <el-drawer
  176. ref="categoryDrawer"
  177. class="category-drawer"
  178. :visible.sync="showCategoryDrawer"
  179. direction="ttb"
  180. :withHeader="false">
  181. <div class="drawer-category-one">
  182. <div
  183. class="drawer-category-one-item"
  184. :class="currentDrawerCategoryIndex === 0 ? 'active' : ''"
  185. @click="handleClickDrawerCategoryOne(0)">
  186. 全部
  187. </div>
  188. <div
  189. class="drawer-category-one-item"
  190. :class="currentDrawerCategoryIndex === index + 1 ? 'active' : ''"
  191. v-for="(category, index) in skillCate"
  192. :key="`drawer-category-one-${category.value}`"
  193. @click="handleClickDrawerCategoryOne(index + 1)">
  194. {{ category.label }}
  195. </div>
  196. </div>
  197. <div class="drawer-category-two">
  198. <!-- 全部二级分类 -->
  199. <div
  200. class="drawer-category-two-wrapper"
  201. v-show="currentDrawerCategoryIndex === 0">
  202. <div
  203. class="drawer-category-two-item"
  204. :class="currentDrawerCategoryId == category.value ? 'active' : ''"
  205. v-for="category in skillCateAll"
  206. :key="`drawer-category-all-${category.value}`"
  207. @click="handleClickDrawerCategoryTwo(category.value)">
  208. {{ category.label }}
  209. </div>
  210. </div>
  211. <!-- 接口返回的二级分类 -->
  212. <div
  213. class="drawer-category-two-wrapper"
  214. v-for="(category, index) in skillCate"
  215. :key="`drawer-category-two-wrapper-${category.value}`"
  216. v-show="currentDrawerCategoryIndex === index + 1">
  217. <div
  218. class="drawer-category-two-item"
  219. :class="currentDrawerCategoryId == categoryChild.value ? 'active' : ''"
  220. v-for="categoryChild in category.children"
  221. :key="`drawer-category-two-${categoryChild.value}`"
  222. @click="handleClickDrawerCategoryTwo(categoryChild.value)">
  223. {{ categoryChild.label }}
  224. </div>
  225. </div>
  226. </div>
  227. </el-drawer>
  228. </div>
  229. </div>
  230. </template>
  231. <script>
  232. import {mapState} from "vuex"
  233. import DealSeoList from "@/components/skill/dealSeoList"
  234. export default {
  235. name: 'SeoSkillList',
  236. data () {
  237. return {
  238. baseUrl: '',
  239. // firstLoad: true,
  240. isWeixinApp: true,
  241. categoryExpanded: true, // 更多按钮不要,默认为展开状态
  242. showCategoryDrawer: false,
  243. currentDrawerCategoryId: 0,
  244. currentDrawerCategoryIndex: 0
  245. }
  246. },
  247. head() {
  248. const {
  249. title = "",
  250. keyword = "",
  251. description = "",
  252. h1 = "",
  253. canonical = "",
  254. metaLocation
  255. } = this.head || {}
  256. let obj = {
  257. title: title,
  258. meta: [{
  259. name: "keywords",
  260. content: keyword
  261. }, {
  262. name: "description",
  263. content: description
  264. }, {
  265. name: "h1",
  266. content: h1
  267. }],
  268. link: [{rel: "canonical", href: canonical}]
  269. }
  270. if (metaLocation) {
  271. obj.meta.push({name: "location", content: metaLocation})
  272. }
  273. return obj
  274. },
  275. computed: {
  276. ...mapState(["deviceType"]),
  277. showWxHeader () {
  278. return !this.deviceType.app && !this.isWeixinApp &&
  279. (this.deviceType.android || this.deviceType.ios)
  280. },
  281. mainMarginTop () {
  282. if (this.mobile && this.showWxHeader) {
  283. return '64px !important'
  284. } else if (this.mobile) {
  285. return '0px !important'
  286. } else {
  287. return '20px !important'
  288. }
  289. }
  290. },
  291. async asyncData ({...params}) {
  292. let dealDataObj = new DealSeoList(params)
  293. let ans = await dealDataObj.dealData()
  294. return {
  295. ...ans
  296. }
  297. },
  298. mounted () {
  299. this.baseUrl = this.$store.state.domainConfig.siteUrl
  300. this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1
  301. },
  302. methods: {
  303. /** 分页获取技能列表数据 */
  304. _getSkillList () {
  305. const self = this
  306. const data = {
  307. type: 1,
  308. page: this.pagination.page,
  309. page_size: this.pagination.pagesize,
  310. cate_id: this.pagination.selectedCateIdTwo,
  311. status: 2,
  312. owner_type: 1
  313. }
  314. this.pagination.loading = true
  315. this.pagination.noMore = false
  316. this.$axios.$post('/api/sale/saleList', data).then(res => {
  317. if (Number(res.status) === 1) {
  318. let skillList = res.data.list || []
  319. skillList.forEach((item) => {
  320. let imageList = item.image.split(',')
  321. item.coverImage = imageList[0] || ''
  322. imageList.splice(0, 1)
  323. item.imageList = imageList
  324. })
  325. if (self.mobile) {
  326. self.skillList = self.skillList.concat(skillList)
  327. } else {
  328. self.skillList = skillList
  329. }
  330. self.pagination.total = res.data.total
  331. self.pagination.pagesize = res.data.page_size || 9
  332. if (self.pagination.page * self.pagination.pagesize >= self.pagination.total) {
  333. console.log('noMore true', self.pagination)
  334. self.pagination.noMore = true
  335. } else {
  336. console.log('noMore false', self.pagination)
  337. self.pagination.noMore = false
  338. }
  339. }
  340. }).then(() => {
  341. self.pagination.loading = false
  342. })
  343. },
  344. /** 点击展开、收起 */
  345. handleClickExpandCategory () {
  346. this.categoryExpanded = !this.categoryExpanded
  347. },
  348. /** 点击一级分类时 */
  349. handleClickCategoryOne (id) {
  350. if (id === 0) {
  351. // 点击全部时,移除筛选分类
  352. this.pagination.selectedCateIdOne = id
  353. this.pagination.selectedCateIdTwo = ''
  354. this.currentDrawerCategoryId = ''
  355. this.pagination.page = 1
  356. this.skillList = []
  357. window.scroll(0, 0)
  358. this._getSkillList()
  359. return
  360. }
  361. if (this.pagination.selectedCateIdOne !== id) {
  362. this.pagination.selectedCateIdOne = id
  363. }
  364. },
  365. /** 点击二级分类时:移动端 */
  366. handleClickCategoryTwo (id) {
  367. if (this.pagination.selectedCateIdTwo === id) {
  368. this.pagination.selectedCateIdTwo = ''
  369. this.currentDrawerCategoryId = ''
  370. } else {
  371. this.pagination.selectedCateIdTwo = id
  372. this.currentDrawerCategoryId = id
  373. }
  374. this.pagination.page = 1
  375. this.skillList = []
  376. window.scroll(0, 0)
  377. this._getSkillList()
  378. },
  379. /** 分页页码改变时 */
  380. handlePageChange (val) {
  381. window.location.href = `${window.location.origin}${window.location.pathname}?page=${val}`
  382. },
  383. /** mobile 加载更多 */
  384. handleLoadMoreSkill () {
  385. if (this.pagination.loading) {
  386. return
  387. }
  388. this.pagination.page++
  389. this._getSkillList()
  390. },
  391. /** 点击筛选时 */
  392. handleShowCategoryDrawer () {
  393. this.showCategoryDrawer = true
  394. },
  395. /**
  396. * 点击 mobile 分类 drawer 一级分类
  397. */
  398. handleClickDrawerCategoryOne (id) {
  399. if (id === 0) {
  400. this.showCategoryDrawer = false
  401. return
  402. }
  403. if (id !== this.currentDrawerCategoryIndex) {
  404. this.currentDrawerCategoryIndex = id
  405. }
  406. },
  407. /**
  408. * 点击 mobile 分类 drawer 二级分类
  409. */
  410. handleClickDrawerCategoryTwo (id) {
  411. if (this.currentDrawerCategoryId === id) {
  412. this.pagination.selectedCateIdTwo = ''
  413. } else {
  414. this.pagination.selectedCateIdTwo = id
  415. }
  416. this.currentDrawerCategoryId = id
  417. this.showCategoryDrawer = false
  418. this.pagination.page = 1
  419. this.skillList = []
  420. window.scroll(0, 0)
  421. this._getSkillList()
  422. },
  423. /**
  424. * 点击 mobile 的一项技能时
  425. */
  426. handleClickSkillItem (saleId) {
  427. if (this.deviceType.android || this.deviceType.ios) {
  428. // 端跳转
  429. let jumpUrl = `${this.baseUrl}/l/${saleId}`
  430. location.href = `proginn://webview?url=${jumpUrl}`
  431. } else {
  432. // web 跳转
  433. location.href = `/l/${saleId}`
  434. }
  435. }
  436. }
  437. }
  438. </script>
  439. <style lang="scss" scoped>
  440. @import "@/assets/css/skill/list.scss";
  441. </style>
  442. <style lang="scss">
  443. .category-drawer {
  444. .el-drawer {
  445. height: 100vh !important;
  446. .el-drawer__body {
  447. position: relative;
  448. width: 100%;
  449. display: flex;
  450. }
  451. }
  452. .drawer-category-one {
  453. width: 100px;
  454. height: 100vh;
  455. padding-bottom: 34px;
  456. background: #f4f5f9;
  457. overflow-x: hidden;
  458. overflow-y: auto;
  459. -webkit-overflow-scrolling: touch;
  460. &::-webkit-scrollbar {
  461. display: none;
  462. }
  463. .drawer-category-one-item {
  464. width: 100%;
  465. height: 50px;
  466. line-height: 50px;
  467. text-align: center;
  468. font-size: 15px;
  469. font-family: PingFangSC, PingFangSC-Medium;
  470. font-weight: 500;
  471. color: #222222;
  472. background: inherit;
  473. &.active {
  474. color: #308eff;
  475. background: #ffffff;
  476. }
  477. }
  478. }
  479. .drawer-category-two {
  480. width: calc(100% - 100px);
  481. height: 100vh;
  482. padding: 4px 10px 34px;
  483. background: #ffffff;
  484. overflow-x: hidden;
  485. overflow-y: auto;
  486. -webkit-overflow-scrolling: touch;
  487. &::-webkit-scrollbar {
  488. display: none;
  489. }
  490. .drawer-category-two-wrapper {
  491. width: 100%;
  492. display: flex;
  493. flex-wrap: wrap;
  494. .drawer-category-two-item {
  495. margin: 8px 8px 0 0;
  496. padding: 0 12px;
  497. height: 35px;
  498. line-height: 35px;
  499. background: rgba(244,245,249,.8);
  500. border-radius: 4px;
  501. // opacity: 0.8;
  502. font-size: 13px;
  503. font-family: PingFangSC, PingFangSC-Regular;
  504. font-weight: 400;
  505. color: #222222;
  506. &.active {
  507. height: 33px;
  508. line-height: 33px;
  509. border: 1px solid #308eff;
  510. background: #ffffff;
  511. font-size: 12px;
  512. font-family: PingFangSC, PingFangSC-Medium;
  513. font-weight: 500;
  514. color: #308eff;
  515. }
  516. }
  517. }
  518. }
  519. }
  520. .wx-header-custom-list {
  521. position: fixed !important;
  522. z-index: 11 !important;
  523. }
  524. </style>