在上篇文章我们将项目头部模块进行了编写与数据渲染。
本篇文章我们进一步深入项目设计评价组件。
分析页面
如图所示,点菜,评价,商家,为导航,我们点击评价的时候,直接跳转评价页面。
评价页面由商家评分一栏,评论列表构成,评论列表支持:全部,有图,点评三种筛选。
综上我们现在开始设计评论组件:
建立组件文件夹
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…
通过计算属性将数据传入 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() 等理解。
以上就是本篇全部内容,下篇我们将会细化商品展示页面,我们下篇见。