Vue+koa2开发一款全栈小程序(8.图书列表页)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue+koa2开发一款全栈小程序(8.图书列表页)相关的知识,希望对你有一定的参考价值。

1.图书列表页获取数据

1.在server/routes/index.js中新增路由

router.get(‘/booklist‘,controllers.booklist)

2.在server/controllers下新增booklist.js

const {mysql}=require(‘../qcloud‘)

module.exports=async(ctx)=>{
    const books=await mysql(‘books‘).select(‘*‘).orderBy(‘id‘,‘desc‘)
    ctx.state.data={
        list:books
    }
}

3.在mydemo/src/pages/books/index.vue中

<template>
    <div>
        <div :key="book.id" v-for="book in books">{{book.title}}</div>
    </div>
</template>
<script>
import {get} from @/until

export default {
    data(){
        return {
            books:[]
        }
    },

    methods:{
        async getList(){
            const books=await get(/weapp/booklist)
            
            this.books=books.data.list
        }
    },

    mounted(){
        this.getList()
    }

}
</script>
<style>
    
</style>

效果图

技术分享图片

 

 2.图书卡片显示数据

 1.在src/components目录下新建组件BookList.vue

<template>
    <div class=‘book-card‘>
        <div class="thumb">
            <img :src="book.image" class="img" mode="aspectFit">
        </div>
        <div class="detail">
            <div class="row">
                <div class="right">
                    {{book.rate}}
                </div>
                <div class="left">
                    {{book.title}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    浏览量
                </div>
                <div class="left">
                    {{book.author}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    添加人
                </div>
                <div class="left">
                    {{book.publisher}}
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    props:[book]
    
}
</script>

<style lang=‘scss‘ scoped>
    .book-card{
        padding: 5px;
        overflow: hidden;
        margin-top: 5px;
        margin-bottom: 5px;
        font-size: 14px;
        .thumb{
            width: 90px;
            height: 90px;
            float: left;
            margin: 0 auto;
            overflow: hidden;
            .img{
                max-width: 100%;
                max-height: 100%;
            }
        }
        .detail{
            margin-left: 100px;
            .row{
                line-height: 20px;
                margin-bottom: 3px;
            }
            .right{
                float: right;
            }
            .left{
                margin-right: 80px;
            }
        }
    }

</style>

2.在src/pages/books/index.vue中

<template>
    <div>
        <BookList :key=‘book.id‘ v-for=‘book in books‘ :book=‘book‘></BookList>
              
    </div>
</template>
<script>
import {get} from @/until;

import BookList from@/components/BookList

export default {
    components:{
        BookList
    },

    data(){
        return {
            books:[]
        }
    },

    methods:{
        async getList(){
            const books=await get(/weapp/booklist)
            
            this.books=books.data.list
        }
    },

    mounted(){
        this.getList()
    }

}
</script>
<style>
    
</style>

效果图

技术分享图片

3.星级显示组件的实现

1.在src/components目录下新建组件Rate.vue

<template>
    <div class="rate">
        <span>☆☆☆☆☆</span>
        <div class="hollow" :style=‘style‘>★★★★★</div>
    </div>
</template>
<script>
export default {
    props:{
        value:{type:[Number,String],default:0}
    },
    computed:{
        style(){
            return `width:${this.value/2}em` 
        }
    },
    
}
</script>
<style lang=‘scss‘>
    .rate{
        position: relative;
        display: inline-block;
        .hollow{
            position: absolute;
            display: inline-block;
            top:0;
            left: 0;
            width: 0;
            overflow: hidden;
        }
    }
</style>

2.在src/components目录下修改BookList.vue

<template>
    <div class=‘book-card‘>
        <div class="thumb">
            <img :src="book.image" class="img" mode="aspectFit">
        </div>
        <div class="detail">
            <div class="row">
                <div class="right">
                    {{book.rate}}<Rate :value=‘book.rate‘></Rate>
                </div>
                <div class="left">
                    {{book.title}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    浏览量
                </div>
                <div class="left">
                    {{book.author}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    添加人
                </div>
                <div class="left">
                    {{book.publisher}}
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import Rate from @/components/Rate
export default {
    components:{
        Rate
    },
    props:[book]
    
}
</script>

<style lang=‘scss‘ scoped>
    .book-card{
        padding: 5px;
        overflow: hidden;
        margin-top: 5px;
        margin-bottom: 5px;
        font-size: 14px;
        .thumb{
            width: 90px;
            height: 90px;
            float: left;
            margin: 0 auto;
            overflow: hidden;
            .img{
                max-width: 100%;
                max-height: 100%;
            }
        }
        .detail{
            margin-left: 100px;
            .row{
                line-height: 20px;
                margin-bottom: 3px;
            }
            .right{
                float: right;
            }
            .left{
                margin-right: 80px;
            }
        }
    }

</style>

效果图

技术分享图片

3.优化UI页面

在src/App.vue中的<style>标签内,加全局样式

.text-primary{
  color: #EA5149;
}

在src/components/BookList.vue中给row加上text-primary类

<div class="row text-primary">
                <div class="right">
                    {{book.rate}}<Rate :value=‘book.rate‘></Rate>
                </div>
                <div class="left">
                    {{book.title}}
                </div>
            </div>

效果图

技术分享图片

4.获取添加人(连表查询)

1.在server/controllers/booklist.js中

const {mysql}=require(‘../qcloud‘)

module.exports=async(ctx)=>{
    const books=await mysql(‘books‘)
    .select(‘books.*‘,‘csessioninfo.user_info‘)
    .join(‘csessioninfo‘,‘books.openid‘,‘csessioninfo.open_id‘)
    .orderBy(‘books.id‘,‘desc‘)
    ctx.state.data={
        list:books.map(v=>{
            const info=JSON.parse(v.user_info)
            return Object.assign({},v,{
                user_info:{
                    nickName:info.nickName
                }
            })
        })
    }
}

2.在src/components/BookList.vue中将原来添加人的位置,替换为

<div class="row">
                <div class="right">
                    {{book.user_info.nickName}}
                </div>
                <div class="left">
                    {{book.publisher}}
                </div>
            </div>

效果图

技术分享图片

 

 5.下拉刷新

1.多添加几本图书入库

2.在src/pages/books目录下,新建main.json

{
    "enablePullDownRefresh":true
  }

3.在src/pages/books/index.vue中

<template>
    <div>
        <BookList :key=‘book.id‘ v-for=‘book in books‘ :book=‘book‘></BookList>
              
    </div>
</template>
<script>
import {get} from @/until;

import BookList from@/components/BookList

export default {
    components:{
        BookList
    },

    data(){
        return {
            books:[]
        }
    },

    methods:{
        async getList(){
            wx.showNavigationBarLoading()
            const books=await get(/weapp/booklist)
            this.books=books.data.list
            wx.stopPullDownRefresh()
            wx.hideNavigationBarLoading()
        }
    },
    
    onPullDownRefresh(){
        // console.log(‘下拉‘)
        this.getList()
    },

    mounted(){
        this.getList()
    }

}
</script>
<style>
    
</style>

6.图书滚动加载功能实现(包含了下拉加载和上拉加载)

1.在server/controllers/booklist.js中修改代码为

const {mysql}=require(‘../qcloud‘)

module.exports=async(ctx)=>{
    const {page}=ctx.request.query
    const size=10
    const books=await mysql(‘books‘)
    .select(‘books.*‘,‘csessioninfo.user_info‘)
    .join(‘csessioninfo‘,‘books.openid‘,‘csessioninfo.open_id‘)
    .limit(size)
    .offset(Number(page)*size)
    .orderBy(‘books.id‘,‘desc‘)
    ctx.state.data={
        list:books.map(v=>{
            const info=JSON.parse(v.user_info)
            return Object.assign({},v,{
                user_info:{
                    nickName:info.nickName
                }
            })
        })
    }
}

2.在src/pages/books/index.vue中修改为

<template>
    <div>
        <BookList :key=‘book.id‘ v-for=‘book in books‘ :book=‘book‘></BookList>
        <p class="text-footer" v-if=‘!more‘>没有更多数据</p>      
    </div>
</template>
<script>
import {get} from @/until;

import BookList from@/components/BookList

export default {
    components:{
        BookList
    },

    data(){
        return {
            books:[],
            page:0,
            more:true
        }
    },

    methods:{
        async getList(){
            
            wx.showNavigationBarLoading()//显示加载中菊花动画
            const books=await get(/weapp/booklist,{page:this.page})
            if(books.data.list.length<10&&this.page>0){
                this.more=false
            }
            this.books=this.books.concat(books.data.list)//下拉刷新,不能直接覆盖books,而是累加
            wx.hideNavigationBarLoading()//隐藏加载中菊花动画
            wx.stopPullDownRefresh()//停止下拉状态
        }
    },
    
    onPullDownRefresh(){
        // console.log(‘下拉‘)
        this.page+=1
        this.getList(true)
    },
    onReachBottom(){
        //上拉(向下到底)
        if(!this.more){
            // 没有更多了
            return false
        }
        this.page+=1
        this.getList()
    },

    mounted(){
        this.getList()
    }

}
</script>
<style>
    
</style>

3.在App.vue中增加样式

.text-footer{
  text-align: center;
  font-size: 15px;
  margin-bottom: 15px;
}

7.图书访问次数统计

1.在src/components/BookList.vue中,修改代码,加上a标签,以及配置

<template>
<a :href="detailUrl">
    <div class=‘book-card‘>
        <div class="thumb">
            <img :src="book.image" class="img" mode="aspectFit">
        </div>
        <div class="detail">
            <div class="row text-primary">
                <div class="right">
                    {{book.rate}}<Rate :value=‘book.rate‘></Rate>
                </div>
                <div class="left">
                    {{book.title}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    浏览量:{{book.count}}
                </div>
                <div class="left">
                    {{book.author}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    {{book.user_info.nickName}}
                </div>
                <div class="left">
                    {{book.publisher}}
                </div>
            </div>
        </div>
    </div>
</a>
</template>

<script>
import Rate from @/components/Rate
export default {
    components:{
        Rate
    },
    props:[book],

    computed:{
        detailUrl(){
            return /pages/detail/main?id=+this.book.id
        }
    }
    
}
</script>

<style lang=‘scss‘ scoped>
    .book-card{
        padding: 5px;
        overflow: hidden;
        margin-top: 5px;
        margin-bottom: 5px;
        font-size: 14px;
        .thumb{
            width: 90px;
            height: 90px;
            float: left;
            margin: 0 auto;
            overflow: hidden;
            .img{
                max-width: 100%;
                max-height: 100%;
            }
        }
        .detail{
            margin-left: 100px;
            .row{
                line-height: 20px;
                margin-bottom: 3px;
            }
            .right{
                float: right;
            }
            .left{
                margin-right: 80px;
            }
        }
    }

</style>

2.在src/pages目录下新建detail目录,新建index.vue和main.js

1.main.js

import Vue from ‘vue‘
import App from ‘./index‘

const app = new Vue(App)
app.$mount()

2.index.vue

<template>
    <div>图书id:{{bookid}}</div>
</template>
<script>

import {get} from @/until

export default {
    data(){
        return{
            bookid:‘‘
        }
    },

    methods:{
        async getDetail(){
            const info=await get(/weapp/bookdetail,{id:this.bookid})
        }
    },

    mounted(){
        this.bookid=this.$root.$mp.query.id //this.$root.$mp.query获取跳转链接传过来的对象集合
        this.getDetail()
    }
}
</script>
<style>

</style>

3.在src/app.json中加入

"pages/detail/main"

技术分享图片

 

 4.在server/routes/index.js中加入路由

router.get(‘/bookdetail‘,controllers.bookdetail)

5.在server/controllers目录下新建bookdetail.js

const {mysql}=require(‘../qcloud‘)

module.exports=async(ctx)=>{
    const {id}=ctx.request.query
    await mysql(‘books‘)
        .where(‘id‘,id)
        .increment(‘count‘,1)
}

6.因为新增了page,所以要重新启动项目

npm run dev

技术分享图片技术分享图片

 

 

 8.排行榜轮播图

1.点击排行榜的获取

1.在src/pages/books/index.vue中增加getTop方法,并在相关位置调用

async getTop(){
            const tops=await get(‘/weapp/top‘)
            this.tops=tops.data.list
            
        }

技术分享图片

2.在server/router/index.js中增加路由

router.get(‘/top‘,controllers.top)

3.在server/controllers目录下新建top.js

const {mysql} =require(‘../qcloud‘)

module.exports=async (ctx)=>{
    const top=await mysql(‘books‘)
                .select(‘id‘,‘title‘,‘image‘,‘count‘)
                .orderBy(‘count‘,‘desc‘)
                .limit(9)
    ctx.state.data={
        list:top
    }
}

2.排行榜轮播图的基本实现

1.在src/components目录下新建组件TopSwiper.vue

<template>
  <div class=‘swiper‘>
      <swiper
      :indicator-dots=‘true‘
      indicator-color=‘#EA5A49‘
      :autoplay=‘true‘
      :interval=‘6000‘
      :duration=‘1000‘
      :circular=‘true‘
    >
    
        <div :key=‘imgindex‘ v-for=‘(top,imgindex) in tops‘>

            <swiper-item>
                <img  class=‘slide-image‘ mode=‘aspectFit‘ :src=‘top.image‘>
            </swiper-item>

        </div>
    

    </swiper>
  </div>
</template>
<script>
export default {
  props:[tops]
  
}
</script>
<style>

</style>

2.在src/pages/books/index.vue中导入TopSwiper组件

<template>
    <div>
        <TopSwiper :tops=‘tops‘></TopSwiper>
        <BookList :key=‘book.id‘ v-for=‘book in books‘ :book=‘book‘></BookList>
        <p class="text-footer" v-if=‘!more‘>没有更多数据</p>      
    </div>
</template>

 

<script>
import {get} from ‘@/until‘;

import BookList from‘@/components/BookList‘
import TopSwiper from ‘@/components/TopSwiper‘

//...

效果图

技术分享图片

 

 3.排行榜轮播图完善实现

1.修改在src/components目录下的组件TopSwiper.vue

<template>
  <div class=‘swiper‘>
      <swiper
      :indicator-dots=‘true‘
      indicator-color=‘#EA5A49‘
      :autoplay=‘true‘
      :interval=‘6000‘
      :duration=‘1000‘
      :circular=‘true‘
    >
    
        <div :key=‘imgindex‘ v-for=‘(top,imgindex) in imgUrls‘>

            <swiper-item>
                <img  
                @click="bookDetail(img)"
                class=‘slide-image‘ 
                mode=‘aspectFit‘
                v-for="img in top"
                :key="img.id"
                :src=‘img.image‘>
            </swiper-item>

        </div>
    

    </swiper>
  </div>
</template>
<script>
export default {
  props:[tops],
  computed:{
      imgUrls(){
        //   如果通用 请用chunk函数 比如lodash的chunk方法
        let res=this.tops
        return [res.slice(0,3),res.slice(3,6),res.slice(6)]
      }
  },
  methods:{
      bookDetail(item){
          wx.navigateTo({
              url:/pages/detail/main?id=+item.id
          })
      }
  }
  
}
</script>
<style lang=‘scss‘>
.swiper{
    margin-top: 5px;
    .slide-image{
        width: 33%;
        height: 250rpx;
    }
}
</style>

效果图

技术分享图片技术分享图片

 

 

2.点击图片预览功能,点击缩略图不会跳转,而是图片预览效果

1.修改在src/components目录下的组件BookList.vue

<template>
<a :href="detailUrl">
    <div class=‘book-card‘>
        <div class="thumb" @click.stop="preview">
            <img :src="book.image" class="img" mode="aspectFit">
        </div>
        <div class="detail">
            <div class="row text-primary">
                <div class="right">
                    {{book.rate}}<Rate :value=‘book.rate‘></Rate>
                </div>
                <div class="left">
                    {{book.title}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    浏览量:{{book.count}}
                </div>
                <div class="left">
                    {{book.author}}
                </div>
            </div>

            <div class="row">
                <div class="right">
                    {{book.user_info.nickName}}
                </div>
                <div class="left">
                    {{book.publisher}}
                </div>
            </div>
        </div>
    </div>
</a>
</template>

<script>
import Rate from @/components/Rate
export default {
    components:{
        Rate
    },
    props:[book],

    computed:{
        detailUrl(){
            return /pages/detail/main?id=+this.book.id
        }
    },
    methods:{
        preview(){
            wx.previewImage({
                current:this.book.image,
                urls:[this.book.image]//轮播图列表
            })
        }
    }
    
}
</script>

<style lang=‘scss‘ scoped>
    .book-card{
        padding: 5px;
        overflow: hidden;
        margin-top: 5px;
        margin-bottom: 5px;
        font-size: 14px;
        .thumb{
            width: 90px;
            height: 90px;
            float: left;
            margin: 0 auto;
            overflow: hidden;
            .img{
                max-width: 100%;
                max-height: 100%;
            }
        }
        .detail{
            margin-left: 100px;
            .row{
                line-height: 20px;
                margin-bottom: 3px;
            }
            .right{
                float: right;
            }
            .left{
                margin-right: 80px;
            }
        }
    }

</style>

效果图

技术分享图片

 

以上是关于Vue+koa2开发一款全栈小程序(8.图书列表页)的主要内容,如果未能解决你的问题,请参考以下文章

Vue+koa2开发一款全栈小程序(3.vue入门Mpvue入门)

Vue+koa2开发一款全栈小程序(5.服务端环境搭建和项目初始化)

全栈项目|小书架|服务器端-NodeJS+Koa2实现首页图书列表接口

微信小程序云开发-从0打造云音乐全栈小程序

零基础快速开发全栈后台管理系统(Vue3+ElementPlus+Koa2)—项目概述篇

Vue3+ElementPlus+Koa2 全栈开发后台系统