list.vue 17 KB

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