前言:接着上一篇項目總結,這一篇是學習過程記錄的最後一篇,這裏會梳理:評論組件、商家組件、優化、打包、相關資料連接。項目github地址:https://github.com/66Web/ljq_eleme,歡迎Star。html
ratings | seller |
1、評論組件-ratings |
評論組件主要分爲三塊
vue
評分信息部分webpack
<div class="ratings-content">
<div class="overview">
<div class="overview-left">
<h1 class="score">{{seller.score}}</h1>
<div class="title">綜合評分</div>
<div class="rank">高於周邊商家{{seller.rankRate}}%</div>
</div>
<div class="overview-right"> ..... </div>
</div>
<split></split>
CSS樣式git
.overview
display flex
padding 18px 0 18px 18px
.overview-left
padding-bottom 6px 0
flex 0 0 137px
width 137px // 防止出現兼容性問題
border-right 1px solid rgba(7,17,27,0.1)
text-align center
@media only screen and (max-width 320px)
flex 0 0 110px
width 110px
.score
margin-bottom 6px
line-height 28px
font-size 24px
color rgb(255, 153, 0)
.title
margin-bottom 8px
line-height 12px
font-size 12px
color rgb(7, 17, 27)
.rank
line-height 10px
font-size 10px
color rgb(147, 153, 159)
.overview-right
flex 1
padding 6px 0 6px 24px
props: {
seller: {
type: Object
}
}
<div class="overview">
<div class="overview-left"> ... </div>
<div class="overview-right">
<div class="score-wrapper">
<span class="title">服務態度</span>
<star :size="36" :score="seller.serviceScore"></star>
<span class="score">{{seller.serviceScore}}</span>
</div>
<div class="score-wrapper">
<span class="title">商品評分</span>
<star :size="36" :score="seller.foodScore"></star>
<span class="score">{{seller.foodScore}}</span>
</div>
<div class="delivery-wrapper">
<span class="title">送達時間</span>
<span class="delivery">{{seller.deliveryTime}}分鐘</span>
</div>
</div>
</div>
CSS樣式:es6
.overview-right
flex 1
padding 6px 0 6px 24px
@media only screen and (max-width 320px)
padding-left 6px
.score-wrapper
line-height 18px
margin-top 8px
font-size 0
.title
display inline-block
vertical-align top
line-height 18px
font-size 12px
color rgb(7, 17, 27)
.star
display inline-block
vertical-align top
margin 0 12px
.score
display inline-block
vertical-align top
line-height 18px
font-size 12px
color rgb(255, 153, 0)
.delivery-wrapper
font-size 0
.title //span文字和文字之間默認是垂直居中的,能夠不用加display vertical-align
display inline-block
vertical-align top
line-height 18px
font-size 12px
color rgb(7, 17, 27)
.delivery
display inline-block
margin-left 12px
vertical-align top
line-height 18px
font-size 12px
color rgb(147, 153, 159)
坑:視口寬度不夠寬時,右側部分過長會出現折行。解決:添加一個mediea Query媒體查詢github
.overview-left
padding-bottom: 6px 0
flex: 0 0 137px
width: 137px // 防止出現兼容性問題
border-right: 1px solid rgba(7,17,27,0.1)
text-align: center
@media only screen and (max-width 320px)
flex: 0 0 110px
width: 110px
.overview-right
flex 1
padding: 6px 0 6px 24px
@media only screen and (max-width 320px)
padding-left: 6px
import star from '@/components/star/star' import BScroll from 'better-scroll'; import split from '@/components/split/split' import ratingselect from '@/components/ratingselect/ratingselect' import {formatDate} from '@/common/js/date'
<template>
<div class="ratings" ref="ratings"> <!-- ratings-content大於ratings的時候出現滾動 -->
<div class="ratings-content">
要實現滾動,像good組件同樣,須要固定視口的高度,將其定位絕對定位,top爲header組件的高度web
.ratings
position: absolute top: 174px
bottom: 0
left: 0
width: 100%
overflow: hidden
評論選擇部分 vue-router
<split></split>
<ratingselect @increment="incrementTotal" :select-type="selectType" :only-content="onlyContent" :ratings="ratings">
</ratingselect>
data () { return { ratings: [], showFlag: false, selectType: ALL, onlyContent: true }; }
注意:此時模板中的ratings是全部商品的ratings(針對一個商家),不是food.vue中對某一種食品的評價——food.ratingsvue-cli
評論詳細信息npm
const ERR_OK = 0; created () { this.$http.get('/api/ratings') .then((res) => { res = res.body; if (res.errno === ERR_OK) { this.ratings = res.data; // console.log(this.ratings)
this.$nextTick(() => { this.scroll = new BScroll(this.$refs.ratings, { click: true }) }); } } )
拿到數據以後在raring組件中填充html中的DOM數據
<div class="rating-wrapper"> <ul> <li v-for="rating in ratings" :key="rating.id" class="rating-item" v-show="needShow(rating.rateType, rating.text)"> <div class="avatar"> <img :src="rating.avatar" width="28px" height="28px"> </div> <div class="content"> <h1 class="name">{{rating.username}}</h1> <div class="star-wrapper"> <star :size="24" :score="rating.score"></star> <span class="delivery" v-show="rating.deliveryTime"> {{rating.deliveryTime}} </span> </div> <p class="text">{{rating.text}}</p> <div class="recommend" v-show="rating.recommend && rating.recommend.length"> <!-- 贊或踩和相關推薦 --> <i class="icon-thumb_up"></i> <span class="item" v-for="item in rating.recommend" :key="item.id">{{item}}</span> </div> <div class="time"> {{rating.rateTime | formatDate}} </div> </div> </li> </ul> </div>
CSS樣式
.rating-wrapper
padding 0 18px
.rating-item
display flex
padding 18px 0
border-1px(rgba(1, 17, 27, 0.1))
.avatar
flex 0 0 28px
width 28px
margin-right 12px
img
border-radius 50%
.content
position relative
flex 1
.name
margin-bottom 4px
line-height 12px
font-weight 700
font-size 10px
color rgb(7, 17, 27)
.star-wrapper
margin-bottom 6px
font-size 0
.star
display inline-block
margin-right 16px
vertical-align top
.delivery
display inline-block
vertical-align top
font-size 10px
line-height 12px
color rgb(147, 153, 159)
.text
line-height 18px
color rgb(7, 17, 27)
font-size 12px
margin-bottom 8px
.recommend
line-height 16px
font-size 0
.icon-thumb_up, .item
display inline-block
margin 0 8px 4px 0
font-size 9px
.icon-thumb_up
color rgb(0, 160, 220)
.item
padding 0 6px
border 1px solid rgba(7, 17, 27, 0.1)
border-radius 1px
color rgb(147, 153, 159)
background #fffff
.time
position absolute
top 0
right 0
line-height 12px
font-size 10px
color rgb(147, 153, 159)
綁定better-scroll,使評論列表部分能夠滾動
2、商家組件-seller |
基礎操做
props: { //APP.vue的routerview中已經將seller傳進來了,這裏只須要接收就好
seller: { type: Object } }
佈局DOM
<div class="overview"> <h1 class="title">{{seller.name}}</h1> <div class="desc border-1px"> <star :size="36" :score="seller.score"></star> <span class="text">({{seller.ratingCount}})</span> <span class="text">月售{{seller.sellCount}}單</span> </div> <ul class="remark"> <li class="block"> <h2>起送價</h2> <div class="content"> <span class="stress">{{seller.minPrice}}</span>元 </div> </li> <li class="block"> <h2>商家配送</h2> <div class="content"> <span class="stress">{{seller.deliveryPrice}}</span>元 </div> </li> <li class="block"> <h2>平均配送時間</h2> <div class="content"> <span class="stress">{{seller.deliveryTime}}</span>元 </div> </li> </ul> <div class="favorite" @click="toggleFavorite($event)"> <i class="icon-favorite" :class="{'active':favorite}"></i> <!-- 對應是否收藏兩種樣式--> <span>{{favoriteText}}</span> <!-- 有沒有選中對應不一樣的文本,因此這裏要綁定一個變量,放到data中 --> </div> </div>
CSS樣式
.seller
position: absolute
top: 174px
bottom: 0
left: 0
width: 100%
overflow: hidden
.overview
padding: 18px
position: relative
.title
margin-bottom: 8px
line-height: 14px
color: rgb(7, 17, 27)
font-size: 14px
.desc
padding-bottom: 18px
font-size: 0
border-1px(rgba(7, 17, 27, 0.1))
&:before
display: none
.star
display: inline-block
vertical-align: top
margin-right: 8px
.text
display: inline-block
vertical-align: top
margin-right: 12px
line-height: 18px // 不能爲父元素設置line-heigth,不然組件會被撐高
font-size: 10px
color: rgb(77, 85, 93)
.remark
display: flex
padding-top: 18px
.block
flex: 1
text-align: center
border-right: 1px solid rgba(7, 17, 27, 0.1)
&:last-child
border: none
h2
margin-bottom: 4px
line-height: 10px
font-size: 10px
color: rgb(147, 153, 149)
.content
line-height: 24px
font-size: 10px
color: rgb(7, 17, 27)
.stress
font-size: 24px
公告與活動部分
<div class="bulletin">
<h1 class="title">公告與活動</h1>
<div class="content-wrapper border-1px">
<p class="content">{{seller.bulletin}}</p>
</div>
<ul v-if="seller.supports" class="supports">
<li class="support-item border-1px"
v-for="(item,index) in seller.supports"
:key="(item.id,index.id)">
<span class="icon" :class="classMap[seller.supports[index].type]"></span>
<span class="text">{{seller.supports[index].description}}</span>
</li>
</ul>
</div>
<split></split>
其中:圖標icon 動態綁定class時,使用classMap,在created()中定義,經過獲取索引值一一對應,同header.vue組件中同樣
created() { this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']; }
.bulletin
padding: 18px 18px 0 18px
.title
margin-bottom: 8px
line-height: 14px
color: rgb(7, 17, 27)
font-size: 14px
.content-wrapper
padding: 0 12px 16px 1px
border-1px(rgba(7, 17, 27, 0.1))
.content
line-height: 24px
font-size: 12px
color: rgb(240, 20, 20)
.supports
.support-item
padding: 16px 12px
border-1px(rgba(7, 17, 27, 0.1))
font-size 0
&:last-child
border-none()
.icon
display inline-block
width 16px
height 16px
vertical-align top
margin-right 6px
background-size 16px 16px
background-repeat no-repeat
&.decrease
bg-image('decrease_4')
&.discount
bg-image('discount_4')
&.guarantee
bg-image('guarantee_4')
&.invoice
bg-image('invoice_4')
&.special
bg-image('special_4')
.text
display inline-block
font-size 12px
line-height 16px
color rgb(7, 17, 27)
使用BScroll
methods: { _initScroll() { this.$nextTick(() => { if (!this.scroll) { this.scroll = new BScroll(this.$refs.seller, {click: true}); }else{ this.scroll.refresh(); } }) }
watch: { 'seller'() { //觀測seller數據的更新,而且執行更新後的操做
this._initScroll(); this._initPics(); } }, created() { this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']; this._initScroll(); this._initPics(); }
緣由:created()的執行時機要先於watch中的seller,而後咱們在執行seller中的initScroll的時候就會發現BScroll已經被初始化了,因此initScroll失效,即便在watch中觀察到變化也只能什麼都不作
解決:必定要爲初始化函數_initScroll()和this._initPics()中的nextTick()下的添加if-else語句,對BScroll進行刷新,完成
<div class="pics">
<h1 class="title">商家實景</h1>
<div class="pic-wrapper" ref="picWrapper">
<ul class="pic-list" ref="picList">
<li class="pic-item" v-for="pic in seller.pics" :key="pic.id">
<img :src="pic" width="120" height="90">
</li>
</ul>
</div>
</div>
<split></split>
CSS樣式:
.pics padding: 18px .title margin-bottom: 12px line-height: 14px color: rgb(7, 17, 27) font-size: 14px .pic-wrapper width: 100% overflow: hidden white-space: nowrap /*不產生折行*/ .pic-list font-size: 0 .pic-item display: inline-block margin-right: 6px width: 120px height: 90px &:last-child margin: 0
_initPics() { if(this.seller.pics) { let picWidth = 120; let margin = 6; let width = (picWidth + margin)*this.seller.pics.length - margin;//計算ul的寬度
this.$nextTick(() => { this.$refs.picList.style.width = width + 'px';//設置ul寬度,不要忘記單位
if (!this.picScroll) { this.picScroll = new BScroll(this.$refs.picWrapper, { scrollX: true,//表示橫向滾動
eventPassthrough:'vertical'//橫向滾動圖片的時候忽略縱向的滾動
}); }else{ this.scroll.refresh(); } }) } }
收藏商家
<div class="favorite" @click="toggleFavorite($event)">
<i class="icon-favorite" :class="{'active':favorite}"></i> <!-- 對應是否收藏兩種樣式-->
<span>{{favoriteText}}</span> <!-- 有沒有選中對應不一樣的文本,因此這裏要綁定一個變量,放到data中 -->
</div>
data() { return { // favorite: false, //默認沒有被收藏,從localStorge中取讀取,不是一個默認值了
favorite: (() => { return loadFromlLocal(this.seller.id, 'favorite', false); })() }; }, computed: { favoriteText() { return this.favorite ? '已收藏' : '收藏'; } }
CSS樣式
.favorite
position: absolute
right: 11px
top: 18px
width: 50px
text-align: center
.icon-favorite
display: block
margin-bottom: 4px
line-height: 24px
font-size: 24px
width: 50px
color: #d4d6d9
&.active
color: rgb(240,20,20)
.text
line-height: 10px
font-size: 10px
color: rgb(77,85,93)
添加點擊事件,methods中定義toggleFavorite()方法
toggleFavorite(event) { if (!event._constructed) { return; } this.favorite = !this.favorite; //這樣寫取法區分商家id,不一樣商家的狀態同樣
//localStorage.favorite = this.favorite;
saveToLocal(this.seller.id, 'favorite', this.favorite); },
保存收藏狀態
/** * 解析url參數 * Created by yi on 2016-12-28. * @return Object {id:12334} */ export function urlParse() { let url = window.location.search; let obj = {}; let reg = /[?&][^?&]+=[^?&]]+/g; let arr = url.match(reg); // ['?id=123454','&a=b']
if (arr) { arr.forEach((item) => { let tempArr = item.substring(1).split('=');// 先分割取到id=123454,以後用=號分開
let key = tempArr[0]; let val = tempArr[1]; obj[key] = val; }); } // return obj;
return {id: 123123}; };
import {urlParse} from './common/js/util.js' data() { return { seller:{ id: (() => { let queryParam = urlParse(); // console.log(queryParam)
return queryParam.id; })() } } }, created: function() { this.$http.get('/api/seller?id=' + this.seller.id) .then((res) => { res = res.body; if (res.errno === ERR_OK) { this.seller = res.data; // console.log(this.seller)
this.seller = Object.assign({}, this.seller, res.data);//擴展對象 添加其它屬性--id
} }, (err) => { }) }
刷新以後,收藏樣式就會消失:建立store.js實現數據的存取,專門存取不一樣商家的id,經過惟一id,將收藏的信息添加到localStorge中
//savaToLocal(this.seller.id, 'favorite', this.favorite);存取
export function saveToLocal(id, key, value) { //存儲到localStorge
let seller = window.localStorage.__seller__; if (!seller) { //沒有seller的時候,初始化,定義一個seller對象,並給他設定一個id
seller = {}; seller[id] = {}; // 每一個id下都是一個單獨的obj
} else { seller = JSON.parse(seller); // JSON 字符串轉換爲對象
if (!seller[id]) { //判斷是否有當前這個商家
seller[id] = {}; } } seller[id][key] = value; // 將key和value存到id這個對象的下邊
//將一個JavaScript值(對象或者數組)轉換爲一個 JSON字符串
window.localStorage.__seller__ = JSON.stringify(seller); } //loadFromlLocal(this.seller.id, 'favorite', false);讀取
export function loadFromlLocal(id, key, def) { //讀取,讀不到的時候傳入一個default變量
let seller = window.localStorage.__seller__; if (!seller) { return def; } seller = JSON.parse(seller)[id]; // 取到這個商家下全部的對象
if (!seller) { return def; } let ret = seller[key]; return ret || def; }
seller.vue中引入,並在data和toggleFavorite()中使用這兩個方法:
import {saveToLocal, loadFromlLocal} from 'common/js/store.js';
data() { return { // favorite: false, //默認沒有被收藏,從localStorge中取讀取,不是一個默認值了
favorite: (() => { return loadFromlLocal(this.seller.id, 'favorite', false); })() }; }
toggleFavorite(event) { if (!event._constructed) { return; } this.favorite = !this.favorite; //這樣寫取法區分商家id,不一樣商家的狀態同樣
//localStorage.favorite = this.favorite;
saveToLocal(this.seller.id, 'favorite', this.favorite); },
3、優化&打包 |
優化
<keep-alive>
<router-view :seller="seller"></router-view>
</keep-alive>
打包
npm run build
4、相關資料連接 |
Vue.js官網:https://vuejs.org.cn/
Vue-cli: https://github.com/vuejs/vue-cli
Vue-resource: https://github.com/vuejs/vue-resource
Vue-router: https://github.com/vuejs/vue-router
better-scroll: http://npm.taobao.org/package/better-scroll
webpack官網:https://www.webpackjs.com/
Stylus中文文檔:https://www.zhangxinxu.com/jq/stylus/
es6入門學習:http://es6.ruanyifeng.com/
eslint規則:http://eslint.org/docs/rules/
設備像素比:https://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/
Flex佈局:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
貝塞爾曲線測試:http://cubic-bezier.com/#.17,.67,.83,.67
注:項目來自慕課網