Design and Implementation of Vue Combat-Evaluation Component

Keywords: Javascript Vue axios Webpack Attribute

In the last article, we wrote and rendered the header module of the project.

In this article, we go further into the project design evaluation component.

Analysis Page

As shown in the figure, when we click on the evaluation, we directly jump to the evaluation page for navigation.

The evaluation page is composed of a column of business scoring, a list of comments, and a list of comments supporting: all, with pictures, comments three kinds of screening.

In summary, we are now starting to design review components:

Create Component Folder

1. Storage of CSS pictures

In view of the variability of the image referenced by the component, we put the image in the component folder for reference. Make components easier to maintain.

2. Path Configuration

In build/webpack.base.conf.js:

alias: {
    'vue$': 'vue/dist/vue.esm.js',//Automatic Completion Settings
    '@': resolve('src'),
    'components': resolve('./src/components')
}

Components are renamed when importing modules through alias rename settings.

Component Writing Practically Required for Import:

// For example, importing Ratings can be written as
    import Ratings from 'components/Ratings/Ratings'

After the path configuration is completed, we set up the Ratings folder and enter:

After sorting out the page structure according to the analysis, we build the page structure first.

In Ratings.vue:

//Set up the container to store the comment component
<template>
    <div class="ratings" ref='ratingView'>
        <div class="ratings-wrapper">
            //Refinement of our components
        </div>
    </div>
</template>

Now we design business scoring, taste, packaging, and other structures as follows:

<div class="overview">
    <div class="overview-left">
        <div class="comment-score">
            <p class="score">{{ratings.comment_score}}</p>
            <p class="text">Business scoring</p>
        </div>
        <div class="other-score">
            <div class="quality-score item">
                <span class="text">Flavor</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">Packing</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">Distribution score</p>
        </div>
    </div>
</div>

Implement the tabs in the comments (all, pictures, comments), list page:

<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>
                //Comment List
                <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">score</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>

The structure is completed, and then we pass in the corresponding data for the component.

Parent-child component communication

Ratings.vue

Import dependent subcomponents:

<script>
    // Import Star components
    import Star from 'components/Star/Star'
    // Import Split components
    import Split from 'components/Split/Split'
    // Import BScroll components
    import BScroll from 'better-scroll';
</script>



    //Setting tab variables
    const ALL = 2; // whole 
    const PICTURE = 1; // With pictures
    const COMMENT = 0; // Comment

Now let's start initializing data and making requests in the create hook.

ratings data section shows:

   
    export default {
        data() {
            return {
                ratings: {},//Store the requested data
                selectType: ALL,//Default Display All
            }
        },
        created() {
            // Initiate get requests through axios
            let that = this;
            this.$axios.get('/api/ratings')
                .then(function(response) { // Access to data
                    var dataSource = response.data;
                    if(dataSource.code == 0) {
                        that.ratings = dataSource.data;//Refer the requested data to data ()
                        // Initialize scrolling
                        that.$nextTick(() => {
                            if(!that.scroll) {
                                that.scroll = new BScroll(that.$refs.ratingView, {
                                    click: true
                                });
                            } else {
                                that.scroll.refresh();
                            }
                        });
                    }

                })
                .catch(function(error) { // Error handling
                    console.log(error);
                });
        }
    }
    
</script>

Note that $refs and setting ref='ratingView'in the container we use BScroll to operate dom, so we use vue's ref API

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

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

                // Refresh operation
                this.$nextTick(() => {
                    this.scroll.refresh();
                });
            },
            fotmatDate(time) {
                let date = new Date(time * 1000);

                // Time format
                let fmt = 'yyyy.MM.dd';

                if(/(y+)/.test(fmt)) { // year
                    let year = date.getFullYear().toString();
                    fmt = fmt.replace(RegExp.$1, year);
                }
                if(/(M+)/.test(fmt)) { // month
                    let mouth = date.getMonth() + 1;
                    if(mouth < 10) {
                        mouth = '0' + mouth;
                    }
                    fmt = fmt.replace(RegExp.$1, mouth);
                }
                if(/(d+)/.test(fmt)) { // day
                    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>');
            }
        }

In methods, we define:

  • Select TypeFn (type) clicks on the switch function executed by the event in the template.
  • fotmatDate (time) sets the time display format function;
  • commentStr (content) inserts text functions;

Note that within the selectTypeFn function, we refresh the list with scroll using the $nextTick () bar after clicking on the corresponding tab.

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

Data is passed into the rating-list template by calculating attributes:

  • The value of selectType determines the data content displayed in the comment list

Again, we need to pay attention to the differences between methods and computational attribute invocation methods. We have compared them before. We need to understand them in detail. Please also read the previous articles or official documents.

        computed: {
            selectComments() {
                if(this.selectType == ALL) { // whole
                    return this.ratings.comments;
                } else if(this.selectType == PICTURE) { // Figure
                    let arr = [];
                    this.ratings.comments.forEach((comment) => {
                        if(comment.comment_pics.length) {
                            arr.push(comment);
                        }
                    });
                    return arr;
                } else { // Comment
                    return this.ratings.comments_dp.comments;
                }
            }
        },

Using the introduced components:

        components: {
            Star,
            Split,
            BScroll
        }


The Split component is the dividing line of the icon above.

Logical Realization of Star Grading

New Star file

Stars are displayed in the form of full stars and half stars. Stars are constructed through the for cycle.

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


Accept the score value from the parent component through props and use it in star.

score in star is processed by calculating attributes.

<script>
    // Star Length
    const LENGTH = 5;
    // Star-to-star correspondence class
    const CLS_ON = 'on';
    const CLS_HALF = 'half';
    const CLS_OFF = 'off';
    
    export default{
        props: {//Pass in score through the parent component and use it as "data ()" within the star component
            score: {
                type: Number//Specified type number
            }
        },
        computed: {
            itemClasses() {
                let result = [];
                // 4.7 => 4.5   3.9 => 3.5  4.1 => 4.0
                // Processing the fraction, take down a multiple of 0.5
                let score = Math.floor(this.score*2) / 2;
                // Decimal, control half-star
                let hasDecimal = score % 1 !== 0;
                // Integer, control the whole star
                let integer = Math.floor(score);
                
                // All star
                for (let i=0; i<integer; i++) {
                    result.push(CLS_ON);
                }
                // Half star
                if(hasDecimal){
                    result.push(CLS_HALF);
                }
                // Make up
                while(result.length < LENGTH){
                    result.push(CLS_OFF);
                }
                
                return result;
            }
        }
    }
</script>

From the page analysis of the evaluation component, we have dismantled a reasonable template structure. Then we configure pictures and the path of component reference, which saves us time in development. Finally, the most important thing is the rendering of data and the realization of star rating. In the process, we once again deepen our understanding of vue's props, methods, computed, $nextTick().

Above is the whole content of this article. In the next part, we will refine the display page of commodities. See you in the next part.

Posted by madk on Wed, 19 Jun 2019 13:11:33 -0700