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>

效果图

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hotqin888的专栏

小程序中搜索文件,阅览pdf,分享文件链接,评论表情符号

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

881
来自专栏更流畅、简洁的软件开发方式

分享一个基于jQuery的锁定表格行列的js脚本。

  网上也有很多锁定行列的方法,一个是使用css,另一个就是专门的控件附带有锁定的功能。css的大多数锁定行,而不能锁定列。好像看过园子里的司徒正美,写过一个用...

2716
来自专栏郭霖

Android最佳性能实践(四)——布局优化技巧

在前面几篇文章当中,我们学习了如何通过合理管理内存,以及高性能编码技巧的方式来提升应用程序的性能。然而实际上界面布局也会对应用程序的性能产生比较大的影响,如果布...

2378
来自专栏Bug生活2048

.net core下对于Excel的一些操作及使用

对于后台相关的管理系统,Excel导出是基本的功能,下面就简单说下实现该功能的代码实现吧

1402
来自专栏我的博客

图片延迟加载

官网:http://www.appelsiini.net/projects/lazyload 使用例子:http://demo.phpfs.com/lazylo...

3245
来自专栏前端说吧

JS-鼠标经过显示二级菜单

44610
来自专栏Create Sun

【.net+jquery】绘制自定义表单(含源码)

前言   两年前的时候就想做一个类似的功能,当时思路大家都讨论好了,诸多原因最终还是夭折了。没想到两年多后再这有重新提出要写一个绘制表单的功能。对此也是有点小激...

8908
来自专栏飞雪无情的博客

Android设计应用图标不用愁---Asset Studio Integration来帮你

Android Asset StudioWeb版是一个非常好用的Andrid图标资源设计工具,它是由RomanNurik开发的。现在我们已经在ADT14里支持同...

1095
来自专栏狂码一生

利用ajaxFileUpload.js实现多文件异步上传功能

  AjaxFileUpload.js是网络开发者写好的插件放出来供大家使用用,原理都是创建隐藏的表单和iframe然后用JS去提交,获得返回值。在这里我将网络...

59613
来自专栏Taylor技术日志

Laravel-Excel导出功能文档

可以在闭包中修改一些属性,很多属性可在配置文件中设置默认值 config/excel.php

1.3K50

扫码关注云+社区