专栏首页大前端666Vue实战系列—评价组件的设计与实现(6)
原创

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

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

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

分析页面

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

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

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

建立组件文件夹

1.css图片的存放

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

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>

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

<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>

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

<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数据部分展示:

​
   
    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/reactivity.html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97

通过计算属性将数据传入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组件就是上图标记的分隔线。

星级评分的逻辑实现

新建Star文件

星星展示形式为 全星,半星,无星 通过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()等理解。

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 009 | 快速入门Web前端开发的正确姿势

    入门标准很简单,就一条:达到能参与 Web 前端实际项目的开发水平。请注意,是实际项目,这就需要了解如今的实际项目开发都用了哪些技术栈。HTML/CSS/Jav...

    Keegan小钢
  • 当前 GitHub 上排名前十的热门 Vue 项目

    Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库,提供了配套设计资源,帮助你的网站快速成型。通过基础的 24 分栏,迅速简便地...

    疯狂的技术宅
  • 推荐6款Vue管理后台框架,收藏好,留备用

    来源 | https://www.jianshu.com/p/0f41bfe211a8

    ConardLi
  • Vue管理后台框架选择推荐

    Element UI 是一套采用 Vue 2.0 作为基础框架实现的组件库,它面向企业级的后台应用,能够帮助你快速地搭建网站,极大地减少研发的人力与时间成本。在...

    用户5807183
  • React 毁了 Web 开发!

    React 是一个很好的库,对于Web开发很重要,因为它引入了声明式与反应式模板,这在当时是每个人都需要的范式转变。当时(也就是6~7年前),我们面临着需要的范...

    开发者技术前线
  • Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(一)基础知识概述

    Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(一)基础知识概述 前言 2016年,我写了一系列的 VUE 入门教程,当...

    FungLeo
  • Vue实战系列—从目录结构谈可扩展架构设计(1)

    好的项目代码结构会大大提升项目的维护性和可扩展性。同时我们可以提供友好的说明,以便其他成员理解项目和快速定位。

    前端大彬哥
  • 他写出了 Vue,却做不对这十道 Vue 笔试题

    请原谅我起了这么个浓浓营销号味道的标题。但这可丝毫没有夸大宣传,而是前端娱乐圈今日份的瓜——

    苏南
  • 个人分享--web前端学习资源分享

    6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天...

    守候i
  • 解读丨飞利浦收购锐珂HCIS业务,好事还是坏事?

    雷锋网按:本文译自HIT Consultant,作者Steve Holloway,现任Signify Research总监。

    AI掘金志
  • 应届渣渣前端面经(还有游戏策划以及鸡汤)

    牛客网
  • 小程序开发框架对比(wepy/mpvue/uni-app/taro)

    uni-app 团队投入两周完成了这个深度评测,下面我们就分享下,实际开发不同框架的测试例时遇到的问题,以及在各端的兼容测试结果。在本文里,我们团队基于真实测...

    码客张
  • 想学Python?这里有一个最全面的职位分析

    Python从2015年开始,一直处于火爆的趋势,目前Python工程师超越Java、Web前端等岗位,起薪在15K左右,目前不管是小公司还是知名大公司都在热招...

    马哥教育
  • Vue实战系列—项目搭建四步曲(0)

    开篇:想通过输出Vue系列文章,让大家全面理解Vue的实现原理,掌握实用技巧,能在实战中使用Vue,解锁一个开发技能。

    前端大彬哥
  • 八个开源的 Spring Boot 学习资源,你值得拥有

    Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 《Spring Boot + Vue 全栈开发实战》迄今为止已经加印了 3 次,...

    JAVA葵花宝典
  • Vue.js高仿饿了么外卖App学习记录

    开发一款vue.js开发一款app,使用vue.js是一款高效的mvvm框架,它轻量,高效,组件化,数据驱动等功能便于开发。使用vue.js开发移动端app,学...

    达达前端
  • 记录下:订单模块初步完成

    哇,好久没有记录自己写代码的总结了,今天记录下,点赞,订阅,转发,感谢各位老铁的支持与厚爱。

    公众号---人生代码
  • Vue:基于Vue2的饿了么实战总结

    MrTreasure
  • 996+裁员潮,什么样的前端人才能安全靠岸?

    据土哥了解,腾讯、阿里、百度、今日头条等大厂目前在招聘Web全栈工程师,薪资已给到了40W+。为什么Web全栈工程师如此受重视?

    闰土大叔

扫码关注云+社区

领取腾讯云代金券