Vue+koa2开发一款全栈小程序(9.图书详情页)

1.获取图书详情

1.修改server/controllers/bookdetail.js为

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

module.exports=async(ctx)=>{
    const {id}=ctx.request.query
    const detail=await mysql('books')
                        .select('books.*','csessioninfo.user_info')
                        .join('csessioninfo','books.openid','csessioninfo.open_id')
                        .where('id',id)
                        .first()
    const info=JSON.parse(detail.user_info)
    ctx.state.data=Object.assign({},detail,{
        user_info:{
            name:info.nickName,
            image:info.avatarUrl
        }
    })

}

2.在mydemo/src/components/BookInfo.vue

<template>
    <div class="bookinfo">
        <div class="thumb">
            <img class="back" 
                :src="info.image"
                mode="aspectFull">
            <img class="img" 
            :src="info.image"
            mode="aspectFit">
            <div class="info">
                <div class="title">
                    {{info.title}}
                </div>
                <div class="author">
                    {{info.author}}
                </div>
            </div>
        </div>  
        <div class="detail">
            <img :src="userinfo.image" class="avatar" mode="aspectFit">
            {{userinfo.name}}
            <div class="right text-primary">
                {{info.rate}}分<Rate :value='info.rate'></Rate>
            </div>
        </div>
        <div class="detail">
            {{info.publisher}}
            <div class="right">
                {{info.price}}
            </div>

        </div>  
    </div>    
</template>
<script>
import Rate from '@/components/Rate'
export default {
    components:{
        Rate
    },
    
    props:['info'],

    computed:{
        userinfo(){
            return this.info.user_info||{}
        }
    }

}
</script>
<style lang="scss">
.bookinfo{
    font-size: 14px;
    .right{
        float: right;
    }
    .detail{
        padding: 5px 10px;
        .avatar{
            width: 20px;
            height: 20px;
            border-radius: 50%;
            vertical-align: middle;
        }
    }
    .thumb{
        width: 750rpx;
        height: 500rpx;
        overflow: hidden;
        position: relative;
        .back{
            filter: blur(15px);
            width: 100%;
        }
        .img{
            position: absolute;
            width: 100%;
            height: 300rpx;
            left: 0;
            top:30rpx;
        }
        .info{
            color: white;
            position:absolute;
            width: 100%;
            left: 0;
            top: 330rpx;
            text-align: center;
            .title{
                font-size: 20px;
            }
            .author{
                font-size: 14px;
            }
        }
    }
}

</style>
View Code

3.在src/pages/detail/index.vue中,加入代码,才可以有分享转发功能

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }

 

index.vue

<template>
<div>

    <BookInfo :info='info'></BookInfo>
</div>
</template>
<script>

import {get} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            bookid:'',
            info:{}
        }
    },

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

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

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style>

</style>
View Code

 

2.图书tags和简介显示

1.在server/controllers/bookdetail.js中修改代码,增添返回tag和简介的代码

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

module.exports=async(ctx)=>{
    const {id}=ctx.request.query
    const detail=await mysql('books')
                        .select('books.*','csessioninfo.user_info')
                        .join('csessioninfo','books.openid','csessioninfo.open_id')
                        .where('id',id)
                        .first()
    const info=JSON.parse(detail.user_info)
    ctx.state.data=Object.assign({},detail,{
        tags:detail.tags.split(','),
        summary:detail.summary.split('\n'),
        user_info:{
            name:info.nickName,
            image:info.avatarUrl
        }
    })

}
View Code

 

2.在mydemo/src/components/BookInfo.vue

<template>
    <div class="bookinfo">
        <div class="thumb">
            <img class="back" 
                :src="info.image"
                mode="aspectFull">
            <img class="img" 
            :src="info.image"
            mode="aspectFit">
            <div class="info">
                <div class="title">
                    {{info.title}}
                </div>
                <div class="author">
                    {{info.author}}
                </div>
            </div>
        </div>  
        <div class="detail">
            <img :src="userinfo.image" class="avatar" mode="aspectFit">
            {{userinfo.name}}
            <div class="right text-primary">
                {{info.rate}}分<Rate :value='info.rate'></Rate>
            </div>
        </div>
        <div class="detail">
            {{info.publisher}}
            <div class="right">
                {{info.price}}
            </div>
        </div>
        <div class="tags">
            <div class="badge" :key="tag" v-for="tag in info.tags">{{tag}}</div>
        </div>  
        <div class="summary">
            <p :key="i" v-for="(sum,i) in info.summary">{{sum}}</p>
        </div>
    </div>    
</template>
<script>
import Rate from '@/components/Rate'
export default {
    components:{
        Rate
    },
    
    props:['info'],

    computed:{
        userinfo(){
            return this.info.user_info||{}
        }
    }

}
</script>
<style lang="scss">
.bookinfo{
    font-size: 14px;
    .right{
        float: right;
    }
    .detail{
        padding: 5px 10px;
        .avatar{
            width: 20px;
            height: 20px;
            border-radius: 50%;
            vertical-align: middle;
        }
    }
    .thumb{
        width: 750rpx;
        height: 500rpx;
        overflow: hidden;
        position: relative;
        .back{
            filter: blur(15px);
            width: 100%;
        }
        .img{
            position: absolute;
            width: 100%;
            height: 300rpx;
            left: 0;
            top:30rpx;
        }
        .info{
            color: white;
            position:absolute;
            width: 100%;
            left: 0;
            top: 330rpx;
            text-align: center;
            .title{
                font-size: 20px;
            }
            .author{
                font-size: 14px;
            }
        }
    }
    .badge{
        display: inline-block;
        margin:5px;
        padding: 5px;
        border-radius: 10px;
        border:1px #EA5A49 solid;
        color: #EA5A49;
    }
    .summary{
        padding: 0 15px;
        margin-top: 10px;
        p{
            text-indent: 2em;
            font-size: 14px;
            margin-top: 5px;
        }
    }
}

</style>
View Code

 

效果图

 

 3.获取手机型号、地理位置,用户评论功能

1.评论框和获取用户手机型号,在src/pages/detail/index.vue中

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <div class="comment">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
    </div>

</div>
</template>
<script>

import {get} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            bookid:'',
            info:{},
            comment:'',
            location:'',
            phone:''
        }
    },

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

        getGeo(){},
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        }
    },

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

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
        }
    }
</style>
View Code

 

 

 

 2.获取经纬度 在src/pages/detail/index.vue中

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <div class="comment">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
    </div>

</div>
</template>
<script>

import {get} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            bookid:'',
            info:{},
            comment:'',
            location:'',
            phone:''
        }
    },

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

        getGeo(e){
            if(e.target.value){
                wx.getLocation({
                    success:geo=>{
                        console.log(geo)
                    }
                })
            }else{
                this.location=''
            }
        },
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        }
    },

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

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
        }
    }
</style>
View Code

 

效果图

 3.通过配合百度地图的api,通过经纬度获取地理位置

1.百度地图开放平台网址:

http://lbsyun.baidu.com/apiconsole/key

2.服务文档和使用方法网址:

http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding

根据使用指南,注册,申请百度开发者,获取服务秘钥(AK)

部分过程图

 

 

 

 

 

3.全球逆地理编码(给经纬度输出地点名称)文档地址:

http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding-abroad

4.在src/pages/detail/index.vue中

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <div class="comment">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
    </div>

</div>
</template>
<script>

import {get} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            bookid:'',
            info:{},
            comment:'',
            location:'',
            phone:''
        }
    },

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

        getGeo(e){
            const ak='你的ak'
            let url='http://api.map.baidu.com/geocoder/v2/'
            if(e.target.value){
                wx.getLocation({
                    success:geo=>{
                        wx.request({
                            url,
                            data:{
                                ak,
                                location:`${geo.latitude},${geo.longitude}`,
                                output:'json'
                            },
                            success:res=>{
                                // console.log(res)
                                if(res.data.status==0){
                                    this.location=res.data.result.addressComponent.city
                                }else{
                                    // console.log('出错了')
                                    this.location='未知地点'
                                }
                            }
                        })
                        // console.log(geo)
                    }
                })
            }else{
                this.location=''
            }
        },
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        }
    },

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

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
        }
    }
</style>
View Code

 

效果图,emmmm,通过经纬度这么定位,而且还是wifi的经纬度,除了城市是准的,其他偏差还挺大的,感觉还是通过ip定位比较靠谱一点,嗯。

 4.点击评论发起请求

1.功能定义:

一个用户只能评论一次

如果已经评论过了,再次访问就看到评论列表,而不是评论输入框

2.在mydemo/src/pages/detail/index/vue中,增加button评论按钮,样式,以及addComment方法

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <div class="comment">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
        <button class="btn" @click="addComment">
            评论
        </button>
    </div>

</div>
</template>
<script>

import {get,post,showModal} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            userinfo:'',
            bookid:'',
            info:{},
            comment:'',
            location:'',
            phone:''
        }
    },

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

        getGeo(e){
            const ak='5hvI2CGwH5YO4ZT85MjpxXgk01WWSGOi'
            let url='http://api.map.baidu.com/geocoder/v2/'
            if(e.target.value){
                wx.getLocation({
                    success:geo=>{
                        wx.request({
                            url,
                            data:{
                                ak,
                                location:`${geo.latitude},${geo.longitude}`,
                                output:'json'
                            },
                            success:res=>{
                                // console.log(res)
                                if(res.data.status==0){
                                    this.location=res.data.result.addressComponent.city
                                }else{
                                    // console.log('出错了')
                                    this.location='未知地点'
                                }
                            }
                        })
                        // console.log(geo)
                    }
                })
            }else{
                this.location=''
            }
        },
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                // console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        },

        async addComment(){
            // 评论内容 手机型号 地理位置 图书id 用户的openid
            const data={
                openid:this.userinfo.openId,
                bookid:this.bookid,
                comment:this.comment,
                phone:this.phone,
                location:this.location
            }
            // console.log(data)
            if(!this.comment){
                return
            }
            try{
               await post('/weapp/addcomment',data) 
               this.comment=''
            }catch(e){
                showModel('失败',e.msg)
            }
            
        }
    },

    mounted(){
        this.bookid=this.$root.$mp.query.id //this.$root.$mp.query获取跳转链接传过来的对象集合
        this.getDetail()
        const userinfo=wx.getStorageSync('userinfo')
        // console.log(888,userinfo)
        if(userinfo){
            this.userinfo=userinfo
        }
    },

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
            margin-bottom: 10px;
        }
    }
</style>
View Code

效果图

 

5.后台接口的实现

1.打开mysql数据库对话框,新建评论表comments

show databases; /*查看数据库列表*/
use cauth; /* 使用cAuth数据库*/

CREATE TABLE `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `openid` varchar(100) NOT NULL,
  `bookid` varchar(100) NOT NULL,
  `comment` varchar(200) NOT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `location` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*创建comments表*/

desc comments; /* 查看comments表的字段*/

select * from comments; /*查看comments的记录*/

 

2.在server/routes/index.js内增加路由

router.post('/addcomment',controllers.addcomment)

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

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

module.exports=async (ctx)=>{
    const {bookid,comment,openid,location,phone}=ctx.request.body
    console.log(bookid,comment,openid,location,phone)
    try{
        await mysql('comments').insert({bookid,comment,openid,location,phone})
        ctx.state.data={
            msg:'succcess'
        }
    }catch(e){
        ctx.state={
            code:-1,
            data:{
                msg:'评论失败:'+e.sqlMessage
            }
        }
    }
}
View Code

效果图

 4.评论列表实现

1.在mydemo/src/pages/detail/index/vue中的mounted中增加获取评论列表this.getComments()方法,在methods中编写

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <div class="comment">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
        <button class="btn" @click="addComment">
            评论
        </button>
    </div>

</div>
</template>
<script>

import {get,post,showModal} from '@/until'

import BookInfo from '@/components/BookInfo'

export default {
    components:{
        BookInfo
    },

    data(){
        return{
            userinfo:'',
            bookid:'',
            info:{},
            comment:'',
            comments:[],
            location:'',
            phone:''
        }
    },

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

        getGeo(e){
            const ak='5hvI2CGwH5YO4ZT85MjpxXgk01WWSGOi'
            let url='http://api.map.baidu.com/geocoder/v2/'
            if(e.target.value){
                wx.getLocation({
                    success:geo=>{
                        wx.request({
                            url,
                            data:{
                                ak,
                                location:`${geo.latitude},${geo.longitude}`,
                                output:'json'
                            },
                            success:res=>{
                                // console.log(res)
                                if(res.data.status==0){
                                    this.location=res.data.result.addressComponent.city
                                }else{
                                    // console.log('出错了')
                                    this.location='未知地点'
                                }
                            }
                        })
                        // console.log(geo)
                    }
                })
            }else{
                this.location=''
            }
        },
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                // console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        },

        async addComment(){
            // 评论内容 手机型号 地理位置 图书id 用户的openid
            const data={
                openid:this.userinfo.openId,
                bookid:this.bookid,
                comment:this.comment,
                phone:this.phone,
                location:this.location
            }
            // console.log(data)
            if(!this.comment){
                return
            }
            try{
               await post('/weapp/addcomment',data) 
               this.comment=''
            }catch(e){
                showModel('失败',e.msg)
            }
            
        },

        async getComments(){
            const comments=await get('/weapp/commentlist',{bookid:this.bookid})
            this.comments=comments
        }
    },

    mounted(){
        this.bookid=this.$root.$mp.query.id //this.$root.$mp.query获取跳转链接传过来的对象集合
        this.getDetail()
        const userinfo=wx.getStorageSync('userinfo')
        // console.log(888,userinfo)
        if(userinfo){
            this.userinfo=userinfo
        }
        this.getComments()
    },

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
            margin-bottom: 10px;
        }
    }
</style>
View Code

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

router.get('/commentlist',controllers.commentlist)

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

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

module.exports=async(ctx)=>{
    const {bookid}=ctx.request.query
    const comments=await mysql('comments')
                            .select('comments.*','cSessionInfo.user_info')
                            .join('cSessionInfo','comments.openid','cSessionInfo.open_id')
                            .where('bookid',bookid)
    ctx.state.data={
        list:comments.map(v=>{
            const info=JSON.parse(v.user_info)
            return Object.assign({},v,{
                title:info.nickName,
                image:info.avatarUrl
            })
        })
    }
}

效果图

 

 

 5.评论列表封装成一个组件

1.在src/components目录下新建CommentList.vue

<template>
    <div class="comment-list">
        <div class="page-title" v-if="comments.length">
            我的评论
        </div>
        <div class="comment" v-for="comment in comments" :key="comment.id">
            <div class="user">
                <div class="inline">
                    <img class="avatar" :src="comment.image" mode='aspectFit'>
                    {{comment.title}}
                </div>
                <div class="right">
                    {{comment.location||'未知地点'}}
                    <span class="text-primary">--</span>
                    {{comment.phone||'未知型号'}}
                </div>
            </div>
            <div style="clear:both;"></div>
            <div class="content">
                {{comment.comment}}
            </div>
        </div>
    </div>
</template>
<script>
export default {
    props:['comments']
}
</script>

<style lang='scss'>
.comment-list{
    background: #eee;
    font-size: 14px;
    .page-title{
        padding-left: 20px;
        background: #eee;
        line-height: 40px;
        font-size: 16px;
        }
    .comment{
        background: white;
        margin-bottom: 10px;
        padding: 5px 20px;
        .content{
            margin: 10px;
        }
        .user{
            .inline{
                float: left;
            }
            .right{
                float:right;
            }
            .avatar{
                width:20px;
                height: 20px;
                border-radius: 50%;
            }
        }
    }
    
}
</style>
View Code

2.在src/pages/detail/index.vue中修改增加代码

<template>
<div>

    <BookInfo :info='info'></BookInfo>

    <CommentList :comments="comments"></CommentList>

    <div class="comment" v-if="showAdd">
        <textarea v-model="comment"
                    class="textarea"
                    :maxlength="100"
                    placeholder="请输入图书短评"></textarea>
        <div class="location">
            地理位置:
            <switch color='#EA5A49' :checked='location' @change="getGeo"></switch>
            <span class="text-primary">{{location}}</span>
        </div>
        <div class="phone">
            手机型号:
            <switch color='#EA5A49' :checked='phone' @change="getPhone"></switch>
            <span class="text-primary">{{phone}}</span>
        </div>
        <button class="btn" @click="addComment">
            评论
        </button>
    </div>

    <div v-else class="text-footer">
        未登录或者已经评论过啦
    </div>

    <button class="btn" open-type="share">分享给好友</button>

</div>
</template>
<script>

import {get,post,showModal} from '@/until'

import BookInfo from '@/components/BookInfo'

import CommentList from '@/components/CommentList'

export default {
    components:{
        BookInfo,
        CommentList
    },

    data(){
        return{
            userinfo:'',
            bookid:'',
            info:{},
            comment:'',
            comments:[],
            location:'',
            phone:''
        }
    },

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

        getGeo(e){
            const ak='5hvI2CGwH5YO4ZT85MjpxXgk01WWSGOi'
            let url='http://api.map.baidu.com/geocoder/v2/'
            if(e.target.value){
                wx.getLocation({
                    success:geo=>{
                        wx.request({
                            url,
                            data:{
                                ak,
                                location:`${geo.latitude},${geo.longitude}`,
                                output:'json'
                            },
                            success:res=>{
                                // console.log(res)
                                if(res.data.status==0){
                                    this.location=res.data.result.addressComponent.city
                                }else{
                                    // console.log('出错了')
                                    this.location='未知地点'
                                }
                            }
                        })
                        // console.log(geo)
                    }
                })
            }else{
                this.location=''
            }
        },
        getPhone(e){
            if(e.target.value){
                const phoneInfo=wx.getSystemInfoSync()
                // console.log(phoneInfo)
                this.phone=phoneInfo.model
            }else{
                // 没选中
                this.phone=''
            }
        },

        async addComment(){
            // 评论内容 手机型号 地理位置 图书id 用户的openid
            const data={
                openid:this.userinfo.openId,
                bookid:this.bookid,
                comment:this.comment,
                phone:this.phone,
                location:this.location
            }
            // console.log(data)
            if(!this.comment){
                return
            }
            try{
               await post('/weapp/addcomment',data) 
               this.comment=''
               this.getComments()
            }catch(e){
                showModel('失败',e.msg)
            }
            
        },

        async getComments(){
            const comments=await get('/weapp/commentlist',{bookid:this.bookid})
            // console.log(11111,comments.data.list[0])
            this.comments=comments.data.list
            
        }
    },

    computed:{
        showAdd(){
            // 没登录
            if(!this.userinfo.openId){
                return false
            }
            if(this.comments.filter(v=>v.openid==this.userinfo.openId).length){
                return false
            }
            return true
        }
    },

    mounted(){
        this.bookid=this.$root.$mp.query.id //this.$root.$mp.query获取跳转链接传过来的对象集合
        this.getDetail()
        const userinfo=wx.getStorageSync('userinfo')
        // console.log(888,userinfo)
        if(userinfo){
            this.userinfo=userinfo
        }
        this.getComments()
        
    },

    onShareAppMessage: (res) => {
        if (res.from === 'button') {
            console.log("来自页面内转发按钮");
            console.log(res.target);
            }
        else {
            console.log("来自右上角转发菜单")
        }
        return {
            // title: '',
            // path: '/pages/share/share?id=123',
            // imageUrl: "/images/1.jpg",
            success: (res) => {
            console.log("转发成功", res);
            },
            fail: (res) => {
            console.log("转发失败", res);
            }
        }
    }



}
</script>
<style lang='scss'>
    .comment{
        margin-top: 10px;
        .textarea{
            width: 730rpx;
            height: 200rpx;
            background: #eee;
            padding: 10rpx;

        }
        .location{
            margin-top: 10px;
        }
        .phone{
            margin-top:10px;
            margin-bottom: 10px;
        }
    }
</style>
View Code

效果图

 

6.修整代码格式

打开cmd,cd到项目目录mydemo下,执行

npm run lint

发现有四处代码不规范,去改正

应该是三个等号写成了两个等号,showModal写错了,写成了showModel

 

posted @ 2018-10-24 00:36  雪落忆海  阅读(624)  评论(1编辑  收藏  举报