Vue实战—评价组件的设计与实现(6)

栏目: JavaScript · 发布时间: 6年前

内容简介:在上篇文章我们将项目头部模块进行了编写与数据渲染。本篇文章我们进一步深入项目设计评价组件。

在上篇文章我们将项目头部模块进行了编写与数据渲染。

本篇文章我们进一步深入项目设计评价组件。

Vue实战—评价组件的设计与实现(6)

分析页面

如图所示,点菜,评价,商家,为导航,我们点击评价的时候,直接跳转评价页面。

评价页面由商家评分一栏,评论列表构成,评论列表支持:全部,有图,点评三种筛选。

综上我们现在开始设计评论组件:

建立组件文件夹

1.css图片的存放

Vue实战—评价组件的设计与实现(6)

针对组件引用的图片可能产生变动性,我们将组件内的图片放入组件文件夹内,进行引用。使得组件更加便于维护。

2.路径配置

build/webpack.base.conf.js内:

alias: {
    'vue$': 'vue/dist/vue.esm.js',//自动补全设置
    '@': resolve('src'),
    'components': resolve('./src/components')
}

通过alias重命名设置对组件导入模块时进行了重命名。

实际在导入需要的组件写法:

// 举个例子,导入Ratings组价可以写成
    import Ratings from 'components/Ratings/Ratings'

图片存放,路径配置完成以后我们建立Ratings文件夹并进入:

根据分析页面结构整理以后所以我们先把页面结构搭建出来:

在Ratings.vue中:

//设置容器存放评论组件
<template>
    <div class="ratings" ref='ratingView'>
        <div class="ratings-wrapper">
            //细化我们组件
        </div>
    </div>
</template>

现在我们设计商家评分,口味,包装,等结构如下图:

Vue实战—评价组件的设计与实现(6)

<div class="overview">
    <div class="overview-left">
        <div class="comment-score">
            <p class="score">{{ratings.comment_score}}</p>
            <p class="text">商家评分</p>
        </div>
        <div class="other-score">
            <div class="quality-score item">
                <span class="text">口味</span>
                <Star :score='ratings.quality_score' class='star'></Star>
                <span class="score">{{ratings.quality_score}}</span>
            </div>
            <div class="pack-score item">
                <span class="text">包装</span>
                <Star :score='ratings.pack_score' class='star'></Star>
                <span class="score">{{ratings.pack_score}}</span>
            </div>
        </div>
    </div>
    <div class="overview-right">
        <div class="delivery-score">
            <p class="score">{{ratings.delivery_score}}</p>
            <p class="text">配送评分</p>
        </div>
    </div>
</div>

实现评论中选项卡(全部,有图,点评),列表页面:

Vue实战—评价组件的设计与实现(6)

<div class="content">
                <div class="rating-select" v-if="ratings.tab">
                    <span class="item" @click="selectTypeFn(2)" :class="{'active':selectType==2}">
                        {{ratings.tab[0].comment_score_title}}
                    </span>
                    <span class="item" @click="selectTypeFn(1)" :class="{'active':selectType==1}">
                        {{ratings.tab[1].comment_score_title}}
                    </span>
                    <span class="item" @click="selectTypeFn(0)" :class="{'active':selectType==0}">
                        <img src="./icon_sub_tab_dp_normal@2x.png" v-show="selectType!=0" />
                        <img src="./icon_sub_tab_dp_highlighted@2x.png" v-show="selectType==0" />
                        {{ratings.tab[2].comment_score_title}}
                    </span>
                </div>

                <div class="labels-view">
                    <span v-for="item in ratings.labels" class="item" :class="{'highligh':item.label_star>0}">
                        {{item.content}}{{item.label_count}}
                    </span>
                </div>
                //评论列表
                <ul class="rating-list">
                    <li v-for="comment in selectComments" class="comment-item">
                        <div class="comment-header">
                            <img :src="comment.user_pic_url" v-if="comment.user_pic_url" />
                            <img src="./anonymity.png" v-if="!comment.user_pic_url" />
                        </div>
                        <div class="comment-main">
                            <div class="user">
                                {{comment.user_name}}
                            </div>
                            <div class="time">
                                {{fotmatDate(comment.comment_time)}}
                            </div>
                            <div class="star-wrapper">
                                <span class="text">评分</span>
                                <Star :score='comment.order_comment_score' class='star'></Star>
                            </div>
                            <div class="c_content" v-html="commentStr(comment.comment)"></div>
                            <div class="img-wrapper" v-if="comment.comment_pics.length">
                                <img v-for="item in comment.comment_pics" :src="item.thumbnail_url" />
                            </div>
                        </div>
                    </li>
                </ul>
            </div>

结构搭建完成,下面我们为组件传入对应的数据。

父子组件通信

Ratings.vue

导入依赖的子组件:

<script>
    // 导入Star组件
    import Star from 'components/Star/Star'
    // 导入Split组件
    import Split from 'components/Split/Split'
    // 导入BScroll组件
    import BScroll from 'better-scroll';
</script>



    //设置选项卡变量
    const ALL = 2; // 全部 
    const PICTURE = 1; // 带图片
    const COMMENT = 0; // 点评

下面我们开始初始化data,在created钩子内发起请求。

ratings数据部分展示:

Vue实战—评价组件的设计与实现(6)

export default {
        data() {
            return {
                ratings: {},//存放请求到的数据
                selectType: ALL,//默认展示全部
            }
        },
        created() {
            // 通过axios发起get请求
            let that = this;
            this.$axios.get('/api/ratings')
                .then(function(response) { // 获取到数据
                    var dataSource = response.data;
                    if(dataSource.code == 0) {
                        that.ratings = dataSource.data;//将请求到的数据引用到data()中
                        // 初始化滚动
                        that.$nextTick(() => {
                            if(!that.scroll) {
                                that.scroll = new BScroll(that.$refs.ratingView, {
                                    click: true
                                });
                            } else {
                                that.scroll.refresh();
                            }
                        });
                    }

                })
                .catch(function(error) { // 出错处理
                    console.log(error);
                });
        }
    }
    
</script>

注意$refs与设置容器中的ref='ratingView'我们用BScroll来操作dom,所以使用了vue的ref API

https://cn.vuejs.org/v2/api/#ref

methods: {
            selectTypeFn(type) {
                this.selectType = type;

                // 刷新操作
                this.$nextTick(() => {
                    this.scroll.refresh();
                });
            },
            fotmatDate(time) {
                let date = new Date(time * 1000);

                // 时间格式
                let fmt = 'yyyy.MM.dd';

                if(/(y+)/.test(fmt)) { // 年
                    let year = date.getFullYear().toString();
                    fmt = fmt.replace(RegExp.$1, year);
                }
                if(/(M+)/.test(fmt)) { // 月
                    let mouth = date.getMonth() + 1;
                    if(mouth < 10) {
                        mouth = '0' + mouth;
                    }
                    fmt = fmt.replace(RegExp.$1, mouth);
                }
                if(/(d+)/.test(fmt)) { // 日
                    let mydate = date.getDate();
                    if(mydate < 10) {
                        mydate = '0' + mydate;
                    }
                    fmt = fmt.replace(RegExp.$1, mydate);
                }

                return fmt;
            },
            commentStr(content) {
                let rel = /#[^#]+#/g;
                return content.replace(rel, '<i>$&</i>');
            }
        }

在methods中我们定义:

  • selectTypeFn(type) 在template中点击事件执行的切换函数;
  • fotmatDate(time)设置时间展示格式函数;
  • commentStr(content)插入文本函数;

注意selectTypeFn函数内在我们点击对应的选项卡后使用 $nextTick()条用scroll刷新列表;

$nextTick() https://cn.vuejs.org/v2/guide...

通过计算属性将数据传入class为rating-list模板中:

  • selectType的值决定了评论列表展示的数据内容

需要再次注意方法与计算属性调用方法等区别,之前我们对比过,需要详细了解,还请阅读之前文章,或官方文档。

computed: {
            selectComments() {
                if(this.selectType == ALL) { // 全部
                    return this.ratings.comments;
                } else if(this.selectType == PICTURE) { // 有图
                    let arr = [];
                    this.ratings.comments.forEach((comment) => {
                        if(comment.comment_pics.length) {
                            arr.push(comment);
                        }
                    });
                    return arr;
                } else { // 点评
                    return this.ratings.comments_dp.comments;
                }
            }
        },

使用引入的组件:

components: {
            Star,
            Split,
            BScroll
        }

Split组件就是上图标记的分隔线。

Vue实战—评价组件的设计与实现(6)

星级评分的逻辑实现

新建Star文件

Vue实战—评价组件的设计与实现(6)

星星展示形式为 全星,半星,无星 通过for循环搭建好star结构:

<template>
    <div class="star">
        <!-- itemClass: on、half、off -->
        <span v-for="itemClass in itemClasses" :class="itemClass" class="star-item">            </span>
    </div>
</template>

通过props接受父组件传来的score值,并在star内使用,

通过计算属性对star内的score进行处理,

<script>
    // 星星长度
    const LENGTH = 5;
    // 星星对应class
    const CLS_ON = 'on';
    const CLS_HALF = 'half';
    const CLS_OFF = 'off';
    
    export default{
        props: {//通过父组件传入score,并且在star组件内作为“data()”使用
            score: {
                type: Number//指定类型 数字
            }
        },
        computed: {
            itemClasses() {
                let result = [];
                // 4.7 => 4.5   3.9 => 3.5  4.1 => 4.0
                // 对分数进行处理,向下取0.5的倍数
                let score = Math.floor(this.score*2) / 2;
                // 小数,控制半星
                let hasDecimal = score % 1 !== 0;
                // 整数,控制全星
                let integer = Math.floor(score);
                
                // 全星
                for (let i=0; i<integer; i++) {
                    result.push(CLS_ON);
                }
                // 半星
                if(hasDecimal){
                    result.push(CLS_HALF);
                }
                // 补齐
                while(result.length < LENGTH){
                    result.push(CLS_OFF);
                }
                
                return result;
            }
        }
    }
</script>

到此我们从评价组件的页面分析,拆出了合理的模板结构,接着配置图片,组件引用的路径,节省了我们在开发中的时间,最后也是最重要的是数据的渲染,以及星级评分的实现。过程中,我们再次加深对vue的props,methods,computed,$nextTick()等理解。

以上就是本篇全部内容,下篇我们将会细化商品展示页面,我们下篇见。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ANSI Common Lisp

ANSI Common Lisp

Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40

For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码