Vue實戰—評價組件的設計與實現(6)

在上篇文章咱們將項目頭部模塊進行了編寫與數據渲染。css

本篇文章咱們進一步深刻項目設計評價組件。html

圖片描述

分析頁面vue

如圖所示,點菜,評價,商家,爲導航,咱們點擊評價的時候,直接跳轉評價頁面。react

評價頁面由商家評分一欄,評論列表構成,評論列表支持:所有,有圖,點評三種篩選。webpack

綜上咱們如今開始設計評論組件:ios

創建組件文件夾web

1.css圖片的存放axios

圖片描述

針對組件引用的圖片可能產生變更性,咱們將組件內的圖片放入組件文件夾內,進行引用。使得組件更加便於維護。api

2.路徑配置app

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

經過計算屬性將數據傳入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()等理解。

以上就是本篇所有內容,下篇咱們將會細化商品展現頁面,咱們下篇見。

相關文章
相關標籤/搜索