框架解析
项目简介
1、主程序入口配置
import App from './App'
import { openWin, redirectTo, backBeaforWin, Ajax } from './utils/common'
import store from './utils/store'
import './css/common.css'
import './css/iconfont.css'
Vue.config.productionTip = false
App.mpType = 'app'
// 把通用方法挂载到Vue原型上
Vue.prototype.$openWin = openWin
Vue.prototype.$redirectTo = redirectTo
Vue.prototype.$backBeaforWin = backBeaforWin
Vue.prototype.$ajax = Ajax
// 把store挂载到Vue原型上
Vue.prototype.$store = store
const app = new Vue(App)
app.$mount()
我们在项目入口里引入公共css、公共方法、以及store,并将公共方法挂载到Vue的原型上
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: '高仿美团',
navigationBarTextStyle: 'black',
// enablePullDownRefresh: true
},
tabBar: {
borderStyle: 'white',
backgroundColor: '#fff',
selectedColor: '#06c1ae',
color: '#666',
list: [{
pagePath: "pages/index/main",
iconPath: 'static/images/tab1.png',
selectedIconPath: 'static/images/tab1-s.png',
text: "首页"
},{
pagePath: "pages/orderList/main",
selectedIconPath: 'static/images/tab2-s.png',
iconPath: 'static/images/tab2.png',
text: "订单"
},{
pagePath: "pages/self/main",
selectedIconPath: 'static/images/tab3-s.png',
iconPath: 'static/images/tab3.png',
text: "我的"
}]
}
根据小程序app.json的配置,我们配置好所有页page、window以及tabBar
2、首页页面
首页有轮播图,图标九宫格,猜你喜欢的商品列表,还有当前城市和搜索面板,其分别是选择城市页面和搜索页面页面的入口
首页页面的页面布局代码为
<div class="container ub-box ub-col">
<dl class="ub-box ub-ver z-padding-v-10-px" style="background:#fff;">
<dd @click.stop="$openWin('/pages/citySelect/main')" class="z-padding-h-10-px ub-box ub-ver">
<span class="z-font-size-14 z-color-666 z-margin-right-3-px">{{curCity}}</span>
<i class="iconfont icon-xiangxiazhankai z-color-666 z-font-size-16"></i>
</dd>
<dd class="ub-flex-1 ub-box ub-ver">
<div @click.stop="$openWin('/pages/search/main')" class="search ub-box ub-ver-v z-width-90-percent z-box-sizing-border">
<i class="iconfont icon-sousuo z-color-666 z-font-size-16"></i>
<span class="z-font-size-14 z-color-999 z-margin-left-8-px">请输入商家名、品类或者商圈...</span>
</div>
</dd>
</dl>
<scroll-view scroll-y style="height: calc(100vh - 50px);" scroll-top="0">
<!--轮播图-->
<div class="ub-box ub-ver z-bg-color-fff">
<swiper class="swiper" indicator-dots="false" autoplay="false" interval="5000" duration="500">
<block v-for="(item, idx) in imgUrls" :key="idx">
<swiper-item>
<image :src="item" class="z-width-100-percent" mode="widthFix"/>
</swiper-item>
</block>
</swiper>
</div>
<!--图标九宫格入口-->
<dl class="ub-box ub-wrap z-padding-v-5-px" style="background:#fff">
<div class="icon-item ub-box ub-col ub-ver" :key="key" v-for="(idx, key) in iconMap">
<dd @click.stop="$openWin('/pages/error/main')" class="icon ub-box ub-ver iconfont" :class="key" :style="{background: iconMap[key]['bk']}"></dd>
<span class="z-padding-v-8-px z-font-size-12 z-color-333">{{iconMap[key]['title']}}</span>
</div>
</dl>
<!--广告-->
<dl class="ub-box ub-wrap z-margin-top-6-px z-padding-v-5-px" style="background:#fff">
<dd @click.stop="$openWin('/pages/error/main')" class="adv ub-flex-1 z-box-sizing-border ub-box ub-ver ub-col">
<span class="z-font-size-14 z-lineHeight-36" style="color:#55a40f">我们约吧</span>
<span class="z-font-size-12 z-color-666">恋人家人好朋友</span>
<img class="z-img-cover" src="/static/images/index1.png">
</dd>
<dd @click.stop="$openWin('/pages/error/main')" class="adv ub-flex-1 z-box-sizing-border z-padding-v-5-px ub-box ub-ver ub-col">
<span class="z-font-size-14 z-lineHeight-36" style="color:#ff3f0d">低价超值</span>
<span class="z-font-size-12 z-color-666">十元惠生活</span>
<img class="z-img-cover" src="/static/images/index2.png">
</dd>
<dd @click.stop="$openWin('/pages/error/main')" class="adv ub-flex-1 z-box-sizing-border z-padding-v-5-px ub-box ub-ver ub-col">
<span class="z-font-size-14 z-lineHeight-36" style="color:#f742a0">午后时光</span>
<span class="z-font-size-12 z-color-666">懒懒下午茶</span>
<img class="z-img-cover" src="/static/images/index3.png">
</dd>
</dl>
<!--猜你喜欢,商品列表-->
<dl class="ub-box ub-col z-margin-top-6-px z-padding-all-8-px" style="background:#fff">
<p class="z-width-100-percent ub-box ub-ver" style="border-bottom:1px solid #eee">
<span class="z-font-size-12 z-color-888 z-lineHeight-36">—猜你喜欢—</span>
</p>
<dd class="ub-box ub-col">
<good v-for="(val, idx) in 7" :key="idx" :isLast="idx===6"></good>
</dd>
</dl>
</scroll-view>
</div>
</template>
<script>
import good from "../../components/good.vue"
export default {
components: {good},
computed: {
curCity () {
return this.$store.state.curCity
}
},
data () {
return {
imgUrls: [
'http://p1.meituan.net/codeman/826a5ed09dab49af658c34624d75491861404.jpg',
'http://p0.meituan.net/codeman/a97baf515235f4c5a2b1323a741e577185048.jpg',
'http://p0.meituan.net/codeman/daa73310c9e57454dc97f0146640fd9f69772.jpg'
],
iconMap: {
'icon-caigou': {title: '美食', bk: '#EF8B3E'},
'icon-shangpin': {title: '猫眼电影', bk: '#E4463B'},
'icon-touchengkongyun': {title: '酒店住宿', bk: '#8B67E5'},
'icon-daohang': {title: '休闲娱乐', bk: '#5DC7B0'},
'icon-zitigui': {title: '外卖', bk: '#F3AE42'},
'icon-jiesuan': {title: 'KTV', bk: '#5DC1A9'},
'.icon-jijianfasong': {title: '丽人', bk: '#EC5B6E'},
'icon-shoucang': {title: '景点门票', bk: '#5CA2F2'},
'icon-baobiao': {title: '火车票', bk: '#FD9D21'},
'icon-pifuzhuti': {title: '民宿', bk: '#BED300'},
},
}
},
methods: {
async initAjax() {
let ret = await this.$ajax({url: 'https://devapi.ynshuke.com/v1/banners'})
console.log(ret)
}
},
mounted() {
// this.initAjax()
},
onPullDownRefresh() {
console.log('onPullDownRefresh');
setTimeout(() => {wx.stopPullDownRefresh()}, 600)
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#e8e8e8;}
.search{background: #f5f5f5;border-radius: 12px;padding: 5px 10px}
.swiper{height: 120px;width: calc(100% - 16px)}
.icon-item{width:20%;padding: 10px 13px 0 13px;box-sizing: border-box;}
.icon{width: 38px;height: 38px;border-radius: 50%;color: #fff;font-size: 24px}
.adv{border-right: 2px solid #eee}
.adv img{width: 50px;height: 50px}
.good{border-bottom: 1px solid #DDD8CE}
.good img{width: 80px;height: 80px}
</style>
其中我们提取封装出商品卡片组件good
<div @click.stop="gotoDetail(good.goodId)" class="card ub-box z-padding-v-10-px" :class="{'z-border-bottom-1-eee':isLast==false}">
<img :src="good.img" class="z-img-cover">
<div class="z-padding-h-10-px ub-flex-1 ub-box ub-col">
<p class="ub-flex-1 ub-box ub-ver ub-between">
<span class="z-width-80-percent z-font-size-15 z-lineHeight-26 z-lines-1-overflow-hidden z-font-weight-bold">{{good.mainTitle}}</span>
<span class="ub-flex-1 z-textAlign-right z-font-size-12 z-color-888">{{good.distance}}km</span>
</p>
<p class="z-font-size-12 z-color-666 z-lineHeight-20">{{good.subTitle}}</p>
<p class="ub-flex-1 ub-box ub-ver ub-between ub-flex-end">
<span class="z-font-size-16" style="color:#06c1ae">{{good.price}}元</span>
<span class="z-font-size-12 z-color-888">已售{{good.sales}}</span>
</p>
</div>
</div>
</template>
<script>
export default {
props: ['curGood', 'isLast'],
data () {
return {
good: {
goodId: '100',
img: 'http://p0.meituan.net/200.0/deal/522fd16a9b25479496188b59476d1b941062402.jpg@206_0_828_828a%7C267h_267w_2e_90Q',
mainTitle: '索菲特大酒店锦厨国际餐厅自助餐',
subTitle: '单人自助晚餐',
distance: '1.7',
price: '308',
sales: '42739',
},
}
},
methods: {
gotoDetail(id) {
wx.navigateTo({url: '/pages/goodDetail/main'})
}
}
}
</script>
<style scoped>
.card img{width: 80px;height: 80px}
</style>
注意我们注入store中的当前城市变量,让其不被跳转影响
首页预览效果如下:
3、城市选择页面
城市选择页面有当前/最近访问城市、热门城市、根据首字母展示所有城市,还有右侧固定住的26个首字母作为点击事件,可以滑动到相应的首字母下的城市列表
我们封装出city组件,其页面布局代码为
<div class="z-width-100-percent">
<scroll-view scroll-y style="height:calc(100vh);" scroll-top="0" :scroll-into-view=currView>
<dl class="ub-box ub-col">
<dd class="z-width-100-percent z-margin-bottom-5-px z-bg-color-fff ub-box ub-ver">
<div class="search ub-box ub-ver-v">
<i class="iconfont icon-sousuo z-color-666 z-font-size-16"></i>
<input class="ub-flex-1 z-font-size-14 z-color-666 z-padding-v-5-px z-margin-left-8-px" placeholder="城市/拼音"/>
</div>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box z-border-bottom-1-eee">
<span id="cc" class="z-font-size-14 z-font-weight-bold z-color-333 z-padding-all-8-px">当前:昆明</span>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box">
<span class="z-font-size-14 z-color-333 z-font-weight-bold z-padding-all-8-px">定位/最近访问</span>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box">
<ul class="ub-box ub-wrap z-padding-all-8-px">
<li @click.stop="clickCity(city)" v-for="(city, idx) in visitCityList" :key="idx" class="hotcity z-font-size-14 z-color-333">{{city.name}}</li>
</ul>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box">
<span class="z-font-size-14 z-color-333 z-font-weight-bold z-padding-all-8-px">热门城市</span>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box">
<ul class="ub-box ub-wrap z-padding-all-8-px">
<li @click.stop="clickCity(city)" v-for="(city, idx) in hotCityList" :key="idx" class="hotcity z-font-size-14 z-color-333">{{city.name}}</li>
</ul>
</dd>
<dd class="z-width-100-percent z-bg-color-fff ub-box">
<span class="z-font-size-14 z-color-333 z-font-weight-bold z-padding-all-8-px">所有城市</span>
</dd>
<dd class="ub-box ub-col">
<div v-for="(val, idx) in cityList" :key="idx" class="z-width-100-percent z-bg-color-fff ub-box ub-col">
<span :id="val.initial" class="ub-flex-1 z-padding-all-8-px z-font-size-14 z-color-888 codeBK">{{val.initial}}</span>
<ul class="ub-box ub-col">
<li @click.stop="clickCity(city)" v-for="(city, i) in val.list" :key="i" class="city ub-flex-1 z-font-size-14 z-color-666">{{city.name}}</li>
</ul>
</div>
</dd>
</dl>
</scroll-view>
<!--fixed部分-->
<dl class="fixList ub-box ub-col ub-ver-v">
<dt class="z-font-size-12 z-margin-bottom-3-px" style="color:#06c1ae">最近热门</dt>
<dd @click.stop="clickCode(val)" v-for="(val, idx) in cityList" :key="idx" class="z-font-size-12" style="margin-bottom:2px;color:#06c1ae;padding:0 50px;">{{val.initial}}</dd>
</dl>
</div>
</template>
<script>
import cityData from "../utils/cityData.js"
export default {
data () {
return {
currView: '',
visitCityList: [
{zip: "010", name: "北京"},
{zip: "021", name: "上海"},
],
hotCityList: [
{zip: "010", name: "北京"},
{zip: "021", name: "上海"},
{zip: "020", name: "广州"},
{zip: "0755", name: "深圳"},
{zip: "022", name: "天津"},
{zip: "028", name: "成都"},
{zip: "0571", name: "杭州"},
{zip: "023", name: "重庆"},
{zip: "025", name: "南京"},
],
cityList: [],
selectCity: {},
}
},
onLoad() {
this.initCityList()
this.currView = ''
},
methods: {
initCityList() {
this.cityList = cityData
},
clickCode(obj){
if (obj.list.length < 1) return
this.currView = obj.initial
},
clickCity(city) {
this.$emit('cityService', city.name)
}
},
}
</script>
<style scoped>
.search{background: #f5f5f5;width: 90%;border-radius: 15px;padding: 0 10px}
.codeBK{background: #f5f5f5}
.hotcity{border:1px solid #f5f5f5;padding: 6px 12px;margin: 0 8px 8px 0}
.city{padding: 10px 8px;border-bottom: 1px solid #f5f5f5}
.fixList{position: fixed;right:5px;top: 12%;z-index: 10;width: 30px;background: transparent;}
</style>
我们引入cityData,并在点击任意城市的时候emit给父组件(即城市选择页面)
对于点击首字母滑动页面到相应首字母对应城市的位置的处理,由于小程序没有任何dom,我们不能使用锚点或者window.scrollTo,但是我们可以利用原生小程序提供的scroll-view中scroll-into-view属性来滚动到相应位置,为此我们只需要用Vue来托管scroll-into-view变量currView即可
最终城市选择页面在真机上显示如下
4、搜索页面
搜索页面有搜索输入框,猜你想找和历史搜索的面板 ,对于动态监听到输入的搜索关键词,以滚动列表的形式展示符合搜索关键词的结果集合,所以猜你想找和历史搜索的面板,与搜索结果列表为互斥关系
搜索页面的页面布局代码为
<div class="container">
<dl class="ub-box ub-col">
<dd class="z-width-100-percent z-padding-all-8-px z-bg-color-fff ub-box ub-ver z-box-sizing-border" style="80px;">
<div class="ub-flex-1 search ub-box ub-ver-v">
<i class="iconfont icon-sousuo z-color-666 z-font-size-16"></i>
<input @input="doInput" @confirm="doSearch" class="ub-flex-1 z-font-size-14 z-color-666 z-padding-v-5-px z-margin-left-8-px" placeholder="保利国际影院"/>
</div>
<span @click.stop="$backBeaforWin()" class="z-font-size-13 z-margin-left-8-px" style="color:#06c1ae">取消</span>
</dd>
<!--搜索值不为空的时候,显示搜索列表-->
<div v-if="searchVal.length>0" class="ub-box ub-col" style="padding:8px 8px 0 8px">
<scroll-view scroll-y style="height: calc(100vh - 80px)" scroll-top="0">
<ul class="ub-box ub-col">
<li @click.stop="clickSearchItem(val)" v-if="currSearchList.length>0" class="search-item ub-box ub-ver z-box-sizing-border" v-for="(val, i) in currSearchList" :key="i">
<i class="iconfont icon-sousuo z-color-999 z-font-size-16 z-margin-right-10-px"></i>
<p class="ub-flex-1 z-color-333 z-font-size-14">{{val.val}}</p>
<span class="z-font-size-12 z-color-999">约{{val.num}}个结果</span>
</li>
<li @click.stop="clickSearchItem(searchVal)" v-if="currSearchList.length===0" class="search-item ub-box ub-ver z-box-sizing-border">
<i class="iconfont icon-sousuo z-color-999 z-font-size-16 z-margin-right-10-px"></i>
<p class="ub-flex-1 z-color-333 z-font-size-14">搜索"{{searchVal}}"</p>
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-color-999 z-font-size-16"></i>
</li>
</ul>
</scroll-view>
</div>
<!--搜索值为空的时候,显示猜你想找和历史搜索-->
<div v-if="searchVal.length===0" class="ub-box ub-col">
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box">
<p class="z-font-size-14 z-color-888">猜你想找</p>
</dd>
<dd class="z-margin-h-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box">
<ul class="ub-box ub-wrap">
<li @click.stop="clickSearchItem(val)" v-for="(val, idx) in guess" :key="idx" class="item z-font-size-13 z-color-333">{{val.name}}</li>
</ul>
</dd>
<dd class="z-margin-h-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between">
<p class="z-font-size-14 z-color-888">历史搜索</p>
<i class="iconfont icon-juqianshou z-color-999 z-font-size-16"></i>
</dd>
<dd class="z-margin-h-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box">
<ul class="ub-box ub-wrap">
<li @click.stop="clickSearchItem(val)" v-for="(val, idx) in history" :key="idx" class="item z-font-size-13 z-color-333">{{val.name}}</li>
</ul>
</dd>
</div>
</dl>
</div>
</template>
<script>
export default {
data () {
return {
searchVal: '',
// 测试搜索结果集
searchAllList: [
{id: '1', val: '我呀便当', num: '7'},
{id: '2', val: '我家黑鱼馆', num: '5'},
{id: '3', val: '我家酸菜鱼', num: '3'},
{id: '4', val: '我家厨房', num: '1'},
{id: '5', val: '我家吃铺', num: '1'},
{id: '6', val: '我的店', num: '2'},
{id: '7', val: '我爱水果', num: '3'},
{id: '8', val: '我家美蛙鱼头', num: '1'},
{id: '9', val: '我家黑鱼', num: '2'},
{id: '10', val: '我的巧克力', num: '1'},
{id: '11', val: '我的公寓', num: '1'},
{id: '12', val: '我香我逸西餐厅', num: '1'},
],
currSearchList: [], // 当前根据搜索关键词搜索到的列表
guess: [
{goodId: '1', name: '北京欢乐谷'}, {goodId: '2', name: '故宫博物院'},
{goodId: '3', name: '北京野生动物园'}, {goodId: '4', name: '古北水镇'},
{goodId: '5', name: '八达岭长城'}, {goodId: '6', name: '北京海洋馆'},
],
history: [
{goodId: '6', name: '北京海洋馆'},
{goodId: '3', name: '北京野生动物园'},
]
}
},
methods: {
doInput(e) {
this.searchVal = e.mp.detail.value
this.filterList()
},
doSearch(e) {
this.searchVal = e.mp.detail.value
},
filterList() {
this.currSearchList = this.searchAllList.filter(item => {
if (item.val.indexOf(this.searchVal) >= 0) return item
})
},
clickSearchItem(val) {
this.$redirectTo('/pages/error/main')
},
},
mounted() {
this.searchVal = ''
this.currSearchList = JSON.parse(JSON.stringify(this.searchAllList))
},
onShow() {
wx.setNavigationBarTitle({title: '搜索'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#fff}
.search{background: #f5f5f5;border-radius: 15px;padding: 0 10px}
.search-item{border-bottom: 1px solid #eee;padding: 15px 0px;}
.item{padding: 8px 10px;background: #f5f5f5;border-radius: 3px;margin: 0 8px 8px 0}
</style>
对于模拟搜索动态列表,我们在data里定义搜索总集合、当前搜索关键词和根据当前搜索关键词匹配到的结果列表
{id: '1', val: '我呀便当', num: '7'},
{id: '2', val: '我家黑鱼馆', num: '5'},
{id: '3', val: '我家酸菜鱼', num: '3'},
{id: '4', val: '我家厨房', num: '1'},
{id: '5', val: '我家吃铺', num: '1'},
{id: '6', val: '我的店', num: '2'},
{id: '7', val: '我爱水果', num: '3'},
{id: '8', val: '我家美蛙鱼头', num: '1'},
{id: '9', val: '我家黑鱼', num: '2'},
{id: '10', val: '我的巧克力', num: '1'},
{id: '11', val: '我的公寓', num: '1'},
{id: '12', val: '我香我逸西餐厅', num: '1'},
],
我们需要监听input的输入事件和完成事件,来维护当前搜索关键词和动态过滤符合搜索关键词的结果集
最终搜索页面在真机上显示如下
5、订单列表页面
订单列表页面比较简单了,只有商品列表的滚动展示
这里为了演示滚动效果,我模拟循环了12次常量数据,订单列表页面的页面布局代码为
<div class="container ub-box">
<scroll-view scroll-y style="height:calc(100vh);" scroll-top="0">
<dl class="z-width-100-percent ub-box ub-col">
<dd @click.stop="$openWin('/pages/orderDetail/main')" v-for="(val, idx) in 12" :key="idx" class="order z-width-100-percent ub-box z-box-sizing-border">
<img class="z-img-cover" :src="order.img" />
<div class="ub-flex-1 z-padding-left-10-px ub-box ub-col">
<span class="z-font-size-15 z-color-333 z-margin-bottom-3-px z-font-weight-bold">{{order.name}}</span>
<span class="z-font-size-12 z-color-888 z-margin-bottom-3-px">数量:{{order.num}}</span>
<span class="z-font-size-12 z-color-888 z-margin-bottom-3-px">总价:{{order.price}}</span>
</div>
<span class="z-font-size-14" style="color:#06c1ae">{{order.type}}</span>
</dd>
</dl>
</scroll-view>
</div>
</template>
<script>
export default {
data () {
return {
order: {
orderId: '100',
img: 'http://p0.meituan.net/200.0/deal/522fd16a9b25479496188b59476d1b941062402.jpg@206_0_828_828a%7C267h_267w_2e_90Q',
name: '索菲特大酒店锦厨国际餐厅自助餐',
num: '1',
price: '308',
type: '待评价'
}
}
},
methods: {},
onShow () {
wx.setNavigationBarTitle({title: '订单列表'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#fff}
.order{border-bottom: 1px solid #f5f5f5;padding: 10px 8px;}
.order img{width: 55px; height: 55px; border-radius: 3px}
</style>
最终订单列表页面在真机上显示如下
6、我的页面
我的页面有登录、退出、以及4个入口项,在点击登录后动态获取用户信息,渲染登录按钮为用户的头像、昵称、性别和居住地信息,点击退出按钮后清除用户信息并替换为登录按钮
我的页面的页面布局代码为
<div class="container ub-box ub-col">
<dl class="ub-box z-padding-all-10-px" style="background:#fff">
<dd class="ub-box ub-ver">
<image :src="userInfo.avatarUrl" class="head-img" mode="aspectFill"></image>
</dd>
<dd class="ub-flex-1 z-font-size-18 z-color-333 ub-box ub-ver-v z-padding-h-10-px">
<button v-if="isLogin===false" class="loginBtn" lang="zh_CN" open-type="getUserInfo" @getuserinfo="onGetUserInfo">登录</button>
<ul v-if="isLogin===true" class="ub-box z-margin-left-10-px ub-col">
<li class="z-font-size-16 z-color-333 z-margin-bottom-5-px">{{userInfo.nickName}}</li>
<li class="z-font-size-14 z-color-888">{{userInfo.province}} {{userInfo.city}} {{userInfo.gender}}</li>
</ul>
</dd>
<dd class="z-font-size-18 z-color-333 ub-box ub-ver-v">
<div @click.stop="exitLogin()" class="exitBtn ub-box ub-ver z-font-size-14">退出</div>
</dd>
</dl>
<dl class="ub-box ub-col z-margin-top-10-px" style="background:#fff;">
<dd @click.stop="gotoOrderList()" class="z-padding-all-10-px ub-box ub-between" style="border-bottom:1px solid #eee">
<p class="ub-box ub-ver">
<i class="iconfont icon-danju" style="color:#06c1ae;font-size:20px"></i>
<span class="z-font-size-15 z-color-666 z-padding-h-10-px">我的订单</span>
</p>
<p class="ub-box ub-ver">
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-14 z-color-888"></i>
</p>
</dd>
<dd @click.stop="$openWin('/pages/error/main')" class="z-padding-all-10-px ub-box ub-between" style="border-bottom:1px solid #eee">
<p class="ub-box ub-ver">
<i class="iconfont icon-hongbao" style="color:#06c1ae;font-size:20px"></i>
<span class="z-font-size-15 z-color-666 z-padding-h-10-px">我的卷包</span>
</p>
<p class="ub-box ub-ver">
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-14 z-color-888"></i>
</p>
</dd>
<dd @click.stop="$openWin('/pages/error/main')" class="z-padding-all-10-px ub-box ub-between" style="border-bottom:1px solid #eee">
<p class="ub-box ub-ver">
<i class="iconfont icon-caiwu" style="color:#06c1ae;font-size:20px"></i>
<span class="z-font-size-15 z-color-666 z-padding-h-10-px">我的抽奖</span>
</p>
<p class="ub-box ub-ver">
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-14 z-color-888"></i>
</p>
</dd>
<dd @click.stop="clickCall()" class="z-padding-all-10-px ub-box ub-between">
<p class="ub-box ub-ver">
<i class="iconfont icon-dianhua" style="color:#06c1ae;font-size:20px"></i>
<span class="z-font-size-15 z-color-666 z-padding-h-10-px">客服电话:10107888</span>
</p>
</dd>
</dl>
</div>
</template>
<script>
export default {
computed: {
isLogin() {
return this.$store.state.isLogin
},
userInfo () {
return this.$store.state.userInfo
}
},
data () {
return {}
},
methods: {
onGetUserInfo (e) {
this.$store.commit('updateIsLogin', true)
this.$store.commit('updateUser', e.mp.detail.userInfo)
},
exitLogin() {
this.$store.commit('updateIsLogin', false)
this.$store.commit('cleanUserInfo')
},
gotoOrderList() {
wx.switchTab({url: '/pages/orderList/main'})
},
clickCall() {
wx.showActionSheet({
itemList: ['客服电话:10107888'],
success(res) {
switch(res.tapIndex) {
case 0:
wx.makePhoneCall({phoneNumber: '10107888'})
break
}
}
})
}
},
onShow () {
wx.setNavigationBarTitle({title: '我的'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#e8e8e8;object-fit: cover}
.head-img{width:70px;height:70px;border-radius:50%;box-shadow:0 0 5px rgba(0,0,0,.2);background:#eee}
.loginBtn{font-size:14px;color:#fff;padding:0px 20px;margin-left: 10px;background: #ff5722}
.exitBtn{border: 1px solid #06c1ae;padding:7px 15px;color:#06c1ae;border-radius: 3px}
</style>
我们引入store来在用户点击登录或者退出的时候,动态维护用户登录状态和用户信息
最终我的页面在真机上显示如下
7、商品详情页面
商品详情页面有商品主图价格信息、映像标签、商家信息、套餐信息、购买须知、部分用户评论和相关商品推荐板块,页面里有两个其它页的入口,分别是提交订单和用户评论列表入口
商品详情页面的页面布局代码为
<div class="container ub-box ub-col ub-ver">
<scroll-view scroll-y style="height: 100vh" scroll-top="0">
<dl class="ub-box ub-col">
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff">
<image @click.stop="previewImage([indexImg])" class="z-width-100-percent z-img-cover indexImg" :src="indexImg">
<div class="indexImg-bk ub-box ub-col">
<span class="z-font-size-18 z-lineHeight-30 z-color-fff z-box-sizing-border z-padding-h-8-px">{{mainTitle}}</span>
<span class="z-font-size-14 z-color-fff z-box-sizing-border z-padding-h-8-px">{{subTitle}}</span>
</div>
</image>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="ub-box ub-ver">
<span class="z-font-size-24 z-margin-right-5-px" style="color:#06c1ae">¥{{nowPrice}}</span>
<span class="z-font-size-13 z-color-888">门市价:¥{{normalPrice}}</span>
</p>
<p class="ub-box ub-ver">
<span @click.stop="$openWin('/pages/submit/main')" class="buyBtn ub-box ub-ver z-font-size-16 z-color-fff">
立即抢购
</span>
</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between">
<p class="ub-box ub-ver">
<i class="iconfont icon-xianshikejian z-font-size-18 z-margin-right-5-px" style="color:#06c1ae;"></i>
<span class="z-font-size-12 z-color-888">过期自动退</span>
</p>
<p class="ub-box ub-ver">
<i class="iconfont icon-yonghu z-font-size-16 z-margin-right-5-px" style="color:#888;"></i>
<span class="z-font-size-12 z-color-888">已售420</span>
</p>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="ub-box ub-ver">
<span class="z-font-size-13 z-color-888">
<star></star>
</span>
</p>
<p @click.stop="$openWin('/pages/comment/main')" class="ub-box ub-ver">
<span class="z-font-size-13 z-color-888">{{commentsNum}}条评论</span>
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-12 z-color-888"></i>
</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff ub-box ub-wrap" style="padding:12px 8px 8px 8px">
<span v-for="(val, idx) in labels" :key="idx" class="label z-font-size-13" :class="{'tuijian': val.type==1, 'butuijian': val.type==0}">{{val.name}} {{val.num}}</span>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-888">商家信息</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between">
<p class="ub-flex-1 ub-box ub-col" style="border-right: 1px solid #eee">
<span class="z-font-size-14 z-lineHeight-24 z-color-333">{{sellerName}}</span>
<span class="z-font-size-13 z-color-999">{{sellAdress}}</span>
</p>
<p @click.stop="clickCall()" class="ub-box ub-ver z-padding-h-8-px">
<i class="iconfont icon-dianhua z-font-size-20" style="color:#06c1ae"></i>
</p>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-888">套餐</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-333">{{package.packageName}}</p>
<p class="ub-box">
<span class="z-font-size-14 z-color-333 z-margin-right-5-px">1位</span>
<span class="z-font-size-14 z-color-333">¥{{normalPrice}}</span>
</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-ver" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-888">备注</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-col">
<ul class="ub-box ub-col">
<li v-for="(val, idx) in package.notes" :key="idx" class="z-font-size-14 z-color-333 z-box-sizing-border z-lineHeight-24">{{val}}</li>
</ul>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-888">购买须知</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-col">
<p class="z-font-size-14 z-lineHeight-30" style="color:#f90">有效期</p>
<p class="z-font-size-14 z-color-888">{{purchaseInfo.validityDate}}</p>
<p class="z-font-size-14 z-lineHeight-30" style="color:#f90">不可用日期</p>
<p class="z-font-size-14 z-color-888">{{purchaseInfo.unavailableDate}}</p>
<p class="z-font-size-14 z-lineHeight-30" style="color:#f90">使用规则</p>
<ul class="ub-box ub-col" >
<li v-for="(val, idx) in purchaseInfo.rules" :key="idx" class="z-font-size-14 z-color-888 z-box-sizing-border z-lineHeight-24">{{val}}</li>
</ul>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="ub-box ub-ver">
<span class="z-font-size-14 z-color-888">
<star></star>
</span>
</p>
<p @click.stop="$openWin('/pages/comment/main')" class="ub-box ub-ver">
<span class="z-font-size-14 z-color-888">{{commentsNum}}条评论</span>
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-12 z-color-888"></i>
</p>
</dd>
<dd class="z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between">
<ul class="ub-flex-1 ub-box ub-col">
<li v-for="(val, idx) in comments" :key="idx" class="z-border-bottom-1-eee">
<comment :comment="val" :isShowLike="false"></comment>
</li>
</ul>
</dd>
<dd @click.stop="$openWin('/pages/comment/main')" class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-h-8-px ub-box ub-between">
<p class="z-margin-bottom-8-px ub-box ub-ver">
<span style="color:#06c1ae" class="z-font-size-14 z-color-888">查看全部用户评价</span>
</p>
<p class="ub-box ub-ver">
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-13 z-color-888"></i>
</p>
</dd>
<dd class="z-margin-top-8-px z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-between" style="border-bottom: 1px solid #f5f5f5">
<p class="z-font-size-14 z-color-888">相关推荐</p>
</dd>
<dd class="z-width-100-percent z-box-sizing-border z-bg-color-fff z-padding-all-8-px ub-box ub-col">
<good v-for="(val, idx) in 2" :key="idx" :isLast="idx==1"></good>
</dd>
</dl>
</scroll-view>
</div>
</template>
<script>
import good from "../../components/good.vue"
import star from "../../components/star.vue"
import comment from "../../components/comment.vue"
export default {
components: {good, star, comment},
data () {
return {
indexImg: 'http://p0.meituan.net/deal/522fd16a9b25479496188b59476d1b941062402.jpg',
mainTitle: '索菲特大酒店锦厨国际餐厅自助餐',
subTitle: '单人自助晚餐',
nowPrice: '308',
normalPrice: '398',
labels: [
{name: '海鲜棒', num: '202', type: '1'},
{name: '回头客', num: '97', type: '1'},
{name: '干净卫生', num: '40', type: '1'},
{name: '上菜快', num: '22', type: '1'},
{name: '不推荐', num: '29', type: '0'},
{name: '家庭聚餐', num: '13', type: '0'},
{name: '朋友聚餐', num: '9', type: '0'},
{name: '闺蜜聚会', num: '5', type: '0'},
{name: '现做现卖', num: '4', type: '0'},
{name: '请客', num: '4', type: '0'},
],
sellerName: '索菲特大酒店锦厨国际餐厅自助餐',
sellAdress: '西山区环城南路777号昆明索菲特大酒店49楼',
package: {
packageName: '单人自助晚餐',
notes: [
'单人自助晚餐:周一至周四18:00-21:00',
'餐厅预定入口: https://tableplus.accorplus.com?vc=KweeZeenRestaurantAccorASE1187',
'锦厨国际餐厅位于昆明索菲特大酒店49楼,能够360度鸟瞰春城美景。',
'我们为您提供400种以上的餐食自助,全场酒水畅饮,包括葡萄酒、各色软饮及果汁。',
'更有专属定制化服务:凡周年纪念日/生日当天到店就餐的客人,将免费提供蛋糕一个(此项仅针对提前至少一天进行预约并说明过情况的客人。)',
'温馨提示:儿童按身高收费,具体是0-110cm儿童免费;110cm-140cm儿童半价;140cm以上的全价。 另:如遇特殊活动时,需根据店里实际情况按实际价格补差价。',
],
},
purchaseInfo: {
validityDate: '2016.2.5 至 2018.6.14',
unavailableDate: '周五至周日',
rules: [
'提前2天预订,周末及节假日提前2天以上预订。预订电话:0871-68639888转锦厨国际餐厅',
'锦厨国际餐厅位于昆明索菲特大酒店49楼,能够360度鸟瞰春城美景。',
'我们为您提供400种以上的餐食自助,全场酒水畅饮,包括葡萄酒、各色软饮及果汁。',
'更有专属定制化服务:凡周年纪念日/生日当天到店就餐的客人,将免费提供蛋糕一个(此项仅针对提前至少一天进行预约并说明过情况的客人。)',
'部分菜品因时令原因有所不同,请以店内当日实际供应为准',
'提供免费WiFi',
'停车位收费标准:免费停车',
]
},
commentsNum: 6,
comments: [
{
header: 'https://img.meituan.net/avatar/855458f5c24ab19951f382ee99533ad981495.jpg@37w_37h_1e_1c',
name: 'AqU753874254',
time: '2018-05-07',
star: '4.0',
say: '菜品很多,强烈推荐龙虾,超级棒!每次来都吃撑!很满意的一家自主餐!生日当天海送了蛋糕、服务员“代玉琳”美女,服务态度超级好,热情,人也长的美美哒,给她一个赞! ',
imgs: [
'http://p0.meituan.net/shaitu/40b07a385f90bca838efa48a911bf491253024.jpg',
'http://p0.meituan.net/shaitu/f6af829ff902040fb3225643b2775c1f111115.jpg',
'http://p0.meituan.net/shaitu/e96132da9f76af022d6e521b2265ad70204304.jpg',
'http://p0.meituan.net/shaitu/307d287b8d55f1d67dab188502a684ec158341.jpg',
]
},
{
header: 'https://p0.meituan.net/122.74/mmc/35ad1f9253761ea6ff822b5e659f234f3758.png',
name: 'PPL546030823',
time: '2018-05-06',
star: '4.5',
say: '环境很好,有昆明的夕阳相伴,谢谢美女服务员代玉琳的热忱服务,度过和朋友悠闲的晚餐时光🎈 #煎鹅肝# ',
imgs: [
'http://p0.meituan.net/400.0/shaitu/bc52b03f7f091d6711b8a1ec024a0e6a83730.jpg',
'http://p0.meituan.net/400.0/shaitu/053247a6b8ede53824435f23196971d2124167.jpg',
'http://p0.meituan.net/400.0/shaitu/0783b3f70ab47607593dcba906c7d570147806.jpg',
]
},
],
}
},
methods: {
previewImage(imgs=[], curIdx=0){
wx.previewImage({current: imgs[curIdx], urls: imgs})
},
clickCall() {
wx.showActionSheet({
itemList: ['客服电话:10107888'],
success(res) {
switch(res.tapIndex) {
case 0:
wx.makePhoneCall({phoneNumber: '10107888'})
break
}
}
})
},
},
onShow() {
wx.setNavigationBarTitle({title: this.mainTitle})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#e8e8e8}
.indexImg{height: 170px;position: relative;}
.indexImg-bk{position: absolute;bottom: 0;left: 0;z-index: 1;width: 100%;height: 30%;background: rgba(0,0,0,.3);padding: 5px 0px}
.buyBtn{background: #f90;padding: 8px 12px;border-radius:3px}
.label{border-radius:3px;background: #fff;padding: 3px 5px;margin: 0 5px 5px 0}
.tuijian{color: #f90;border:1px solid #f90;}
.butuijian{color: #999;border:1px solid #ddd;}
</style>
这个页面里用到了卡片组件good,打星评分组件star和评论组件comment,good组件首页已经封装好,下面我们封装出star和comment组件
注意:由于小程序没有伪类,为此我们利用absolute来动态渲染星星的颜色
下面是comment组件的布局代码
<div class="z-width-100-percent ub-box z-padding-v-10-px">
<div class="z-margin-right-10-px">
<img class="z-img-cover" :src="comment['header']" style="width:40px;height:40px;border-radius:50%">
</div>
<ul class="ub-flex-1 ub-box ub-col">
<li class="ub-box ub-between z-margin-bottom-3-px">
<span class="z-font-size-14 z-color-333">{{comment.name}}</span>
<span class="z-font-size-12 z-color-888">{{comment.time}}</span>
</li>
<li class="ub-box ub-between z-margin-bottom-3-px">
<star :isShowNum="false" :num="comment.star"></star>
</li>
<li class="ub-box z-margin-bottom-8-px">
<p class="z-font-size-14 z-color-333 z-lineHeight-22">{{comment.say}}</p>
</li>
<li v-if="comment.imgs.length > 0" class="ub-box z-margin-bottom-8-px">
<div @click.stop="previewImage(comment.imgs, i)" class="z-margin-right-5-px z-width-25-percent" v-for="(img, i) in comment.imgs" :key="i">
<img class="z-img-cover" style="width:100%;height:70px;" :src="img">
</div>
</li>
<li v-if="isShowLike" class="ub-box ub-end">
<div class="like ub-box ub-ver">
<i class="iconfont icon-xihuan z-font-size-14 z-margin-right-5-px z-color-999"></i>
<span class="z-font-size-12 z-color-666">赞</span>
</div>
</li>
</ul>
</div>
</template>
<script>
import star from "./star.vue"
export default {
props: {
comment: {
type: Object
},
isShowLike: {
type: Boolean,
default: true
},
},
components: {star},
data () {
return {}
},
mounted() {},
methods: {
previewImage(imgs=[], curIdx=0){
wx.previewImage({current: imgs[curIdx], urls: imgs})
},
},
}
</script>
<style scoped>
.like{border-radius: 10px; padding: 2px 13px;border:1px solid #eee;}
</style>
最终商品详情页面在真机上显示如下
8、评论列表页面
这个页面是通过商品详情页为入口进入,其评论分为全部、晒图评论、低分评论和最新评论,各个评论的通过swiper滑动切换来更新相应的评论列表
评论列表页面的页面布局为
<div class="container">
<!--评论类型tab栏-->
<dl class="z-width-100-percent ub-box z-box-sizing-border">
<dd @click.stop="clickTab(idx)" v-for="(val, idx) in tabs" :key="idx" :class="{'on': curIdx==idx}" class="ub-flex-1 ub-box ub-ver swiper-item z-box-sizing-border z-font-size-14 z-color-666 z-padding-v-10-px">{{val}}</dd>
</dl>
<!--相应评论类型的tab内容-->
<swiper :current="curIdx" class="swiper-box" duration="300" style="height:calc(100vh - 30px)" @change="bindchange">
<!--全部评论-->
<swiper-item item-id="t1">
<scroll-view scroll-y style="height: calc(100vh - 30px)" scroll-top="0">
<dl class="z-margin-top-10-px ub-box ub-col z-width-100-percent z-padding-h-10-px z-box-sizing-border">
<dd v-if="allComments.length > 0" class="z-border-bottom-1-eee" v-for="(val, idx) in allComments" :key="idx">
<comment :comment="val"></comment>
</dd>
<p v-if="allComments.length === 0" class="ub-box ub-ver z-padding-v-10-px">
<span class="z-font-size-13 z-color-888">暂无评论...</span>
</p>
</dl>
</scroll-view>
</swiper-item>
<!--晒图评论-->
<swiper-item item-id="t2">
<scroll-view scroll-y style="height: calc(100vh - 30px)" scroll-top="0">
<dl class="z-margin-top-10-px ub-box ub-col z-width-100-percent z-padding-h-10-px z-box-sizing-border">
<dd v-if="hasImgComments.length > 0" class="z-border-bottom-1-eee" v-for="(val, idx) in hasImgComments" :key="idx">
<comment :comment="val"></comment>
</dd>
<p v-if="hasImgComments.length === 0" class="ub-box ub-ver z-padding-v-10-px">
<span class="z-font-size-13 z-color-888">暂无评论...</span>
</p>
</dl>
</scroll-view>
</swiper-item>
<!--低分评论-->
<swiper-item item-id="t3">
<scroll-view scroll-y style="height: calc(100vh - 30px)" scroll-top="0">
<dl class="z-margin-top-10-px ub-box ub-col z-width-100-percent z-padding-h-10-px z-box-sizing-border">
<dd v-if="lowScoreComments.length > 0" class="z-border-bottom-1-eee" v-for="(val, idx) in lowScoreComments" :key="idx">
<comment :comment="val"></comment>
</dd>
<p v-if="lowScoreComments.length === 0" class="ub-box ub-ver z-padding-v-10-px">
<span class="z-font-size-13 z-color-888">暂无评论...</span>
</p>
</dl>
</scroll-view>
</swiper-item>
<!--最近评论-->
<swiper-item item-id="t4">
<scroll-view scroll-y style="height: calc(100vh - 30px)" scroll-top="0">
<dl class="z-margin-top-10-px ub-box ub-col z-width-100-percent z-padding-h-10-px z-box-sizing-border">
<dd v-if="lastComments.length > 0" class="z-border-bottom-1-eee" v-for="(val, idx) in lastComments" :key="idx">
<comment :comment="val"></comment>
</dd>
<p v-if="lastComments.length === 0" class="ub-box ub-ver z-padding-v-10-px">
<span class="z-font-size-13 z-color-888">暂无评论...</span>
</p>
</dl>
</scroll-view>
</swiper-item>
</swiper>
</div>
</template>
<script>
import comment from "../../components/comment.vue"
export default {
components: {comment},
data () {
return {
curIdx: 0,
tabs: [],
allComments: [
{
header: 'https://img.meituan.net/avatar/855458f5c24ab19951f382ee99533ad981495.jpg@37w_37h_1e_1c',
name: 'AqU753874254',
time: '2018-05-07',
star: '4.0',
say: '菜品很多,强烈推荐龙虾,超级棒!每次来都吃撑!很满意的一家自主餐!生日当天海送了蛋糕、服务员“代玉琳”美女,服务态度超级好,热情,人也长的美美哒,给她一个赞! ',
imgs: [
'http://p0.meituan.net/shaitu/40b07a385f90bca838efa48a911bf491253024.jpg',
'http://p0.meituan.net/shaitu/f6af829ff902040fb3225643b2775c1f111115.jpg',
'http://p0.meituan.net/shaitu/e96132da9f76af022d6e521b2265ad70204304.jpg',
'http://p0.meituan.net/shaitu/307d287b8d55f1d67dab188502a684ec158341.jpg',
]
},
{
header: 'https://p0.meituan.net/122.74/mmc/35ad1f9253761ea6ff822b5e659f234f3758.png',
name: 'PPL546030823',
time: '2018-05-06',
star: '4.5',
say: '环境很好,有昆明的夕阳相伴,谢谢美女服务员代玉琳的热忱服务,度过和朋友悠闲的晚餐时光🎈 #煎鹅肝# ',
imgs: [
'http://p0.meituan.net/400.0/shaitu/bc52b03f7f091d6711b8a1ec024a0e6a83730.jpg',
'http://p0.meituan.net/400.0/shaitu/053247a6b8ede53824435f23196971d2124167.jpg',
'http://p0.meituan.net/400.0/shaitu/0783b3f70ab47607593dcba906c7d570147806.jpg',
]
},
{
header: 'https://img.meituan.net/122.74/avatar/5311913bf65406ba020854e2436c9ca156391.jpg',
name: 'SWB34889218',
time: '2018-04-18',
star: '5.0',
say: '每次来都吃的好撑!菜品超级多!肖亚萍和茶宏燕两个小姐姐超级热情,超级体贴!',
imgs: []
},
{
header: 'https://p0.meituan.net/122.74/mmc/35ad1f9253761ea6ff822b5e659f234f3758.png',
name: 'smsaikyo',
time: '2018-04-13',
star: '3.8',
say: '真的不错呢【口味】【环境】【服务】',
imgs: []
},
{
header: 'https://img.meituan.net/122.74/avatar/5e1702cffcb2045e5703569dfec6aa5743059.jpg',
name: 'Zjx983004122',
time: '2018-04-12',
star: '3.6',
say: '菜品非常好!环境高大上!如果你爱你的姑娘就带她来索菲特锦厨国际餐厅吧!奥对了我真的很想给服务生李艺鑫六星好评!服务太到位了,像一家人一样!李艺鑫下次因为你的服务而来。',
imgs: ['http://p0.meituan.net/400.0/shaitu/290aad74bf18e7cd1409a62d96bd2106178617.jpg']
},
{
header: 'https://p0.meituan.net/122.74/mmc/35ad1f9253761ea6ff822b5e659f234f3758.png',
name: 'pny1980',
time: '2018-04-10',
star: '1.0',
say: '菜品一般,团购就不给开发票,投诉到底我就不信了。最搞笑的就是用餐当中房顶漏水!',
imgs: []
},
],
hasImgComments: [],
lowScoreComments: [],
lastComments: [],
}
},
methods: {
bindchange(e) {
this.curIdx = e.target.current
},
clickTab(i) {
if (this.curIdx === i) return
this.curIdx = i
},
// 比较日期是否在当前日期的recently范围之间
compareDateToNow(time) {
const compare = new Date(time.replace(/-/g,'\/')).getTime()
const recently = 86400000 * 3 // 定义三天之内
return new Date().getTime() - recently <= compare
},
initAllComments(){
// 晒图评论类型:从全集过滤出有图片的集合
this.hasImgComments = this.allComments.filter(item => {
if(item.imgs.length > 0) return item
})
// 低分评论类型:从全集过滤出分数小于3的集合
this.lowScoreComments = this.allComments.filter(item => {
if((item.star|0) < 3) return item
})
// 最新评论类型:从全集过滤出3天之内的集合
this.lastComments = this.allComments.filter(item => {
if(this.compareDateToNow(item.time)) return item
})
this.curIdx = 0
this.tabs = new Array(4)
this.tabs[0] = '全部(' + this.allComments.length + ')'
this.tabs[1] = '晒图(' + this.hasImgComments.length + ')'
this.tabs[2] = '低分(' + this.lowScoreComments.length + ')'
this.tabs[3] = '最新(' + this.lastComments.length + ')'
},
},
mounted() {
this.initAllComments()
},
onShow () {
wx.setNavigationBarTitle({title: '用户评价'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#fff;}
.swiper-item{border-bottom: 1px solid #ddd;}
.on{color: #06c1ae;border-bottom: 3px solid #06c1ae;}
.like{border-radius: 10px; padding: 2px 13px;border:1px solid #eee;}
</style>
为了演示不同类型评论的切换,我们定义好全集评论列表,并过滤计算出不同类型的数组用于渲染不同的列表
最终评论列表页面在真机上显示如下
9、提交订单页面
这个页面主要有数量的动态增加或者减少,以及价格的动态变化
提交订单页面的页面布局为
<div class="container ub-box">
<dl class="ub-box ub-col z-width-100-percent">
<dd class="item z-width-100-percent ub-box ub-ver ub-between z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-font-weight-bold">{{formdata.mainTitle}}</span>
<span class="z-font-size-15 z-color-333 z-font-weight-bold">{{formdata.price}}元</span>
</dd>
<dd class="item z-width-100-percent ub-box ub-ver ub-between z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-font-weight-bold">数量</span>
<span>
<counter @counterService="counterService"></counter>
</span>
</dd>
<dd class="item z-width-100-percent ub-box ub-ver ub-between z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-font-weight-bold">小计</span>
<span class="z-font-size-15 z-font-weight-bold" style="color:red">
¥{{formdata.totalPrice}}
</span>
</dd>
<dd class="item z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-between z-box-sizing-border z-bg-color-fff">
<p class="ub-box ub-ver">
<i class="iconfont icon-hongbao z-color-666 z-font-size-18 z-margin-right-5-px" style="color:#f90"></i>
<span class="z-font-size-15 z-color-333 z-font-weight-bold">抵用券</span>
</p>
<p class="ub-box ub-ver">
<span class="z-font-size-12 z-color-888 z-margin-right-3-px">无可用抵用券</span>
<i class="iconfont icon-xiayiyeqianjinchakangengduo z-font-size-14 z-color-888"></i>
</p>
</dd>
<dd class="item z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-between z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-font-weight-bold">绑定手机号</span>
<span class="z-font-size-15 z-color-333 z-font-weight-bold">{{formdata.phone}}</span>
</dd>
<!--fixed部分-->
<ul class="fixCon ub-box ub-ver ub-between">
<li class="ub-box ub-ver z-padding-h-10-px">
<span class="z-font-size-12 z-color-999 z-margin-right-10-px">实付金额</span>
<span class="z-font-size-18 z-font-weight-bold" style="color:red">¥{{formdata.totalPrice}}</span>
</li>
<li @click.stop="$openWin('/pages/error/main')" class="sumbitBtn ub-box ub-ver z-font-size-16 z-color-fff">提交订单</li>
</ul>
</dl>
</div>
</template>
<script>
import counter from "../../components/counter.vue"
export default {
components: {counter},
data () {
return {
formdata: {
goodId: '100',
mainTitle: '索菲特大酒店锦厨国际餐厅自助餐',
num: 1,
price: 308.00,
totalPrice: 308.00,
phone: '138****3468',
}
}
},
methods: {
counterService(n) {
this.formdata.num = n
this.formdata.totalPrice = (this.formdata.price|0) * n
}
},
onShow() {
wx.setNavigationBarTitle({title: '提交订单'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#f5f5f5}
.item{border-bottom: 1px solid #f5f5f5;padding: 10px 8px;}
.fixCon{position: fixed;left: 0;bottom: 0;z-index: 10;width: 100%;background: #fff;}
.sumbitBtn{padding: 15px 35px;background: #06c1ae;box-sizing: border-box;}
</style>
由于数量需要动态增减,我们封装出计数组件counter
counter组件的布局为
<div class="ub-box ub-ver-v z-width-100-percent">
<ul class="counter z-width-100-percent ub-box">
<li @click.stop="changeNum(-1)" class="counter-dis ub-box ub-ver z-font-size-16 z-color-666">-</li>
<li class="counter-num ub-box ub-ver z-font-size-14 z-color-333">{{num}}</li>
<li @click.stop="changeNum(1)" class="counter-add ub-box ub-ver z-font-size-16">+</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
num: 1,
}
},
mounted() {},
methods: {
changeNum(type) {
if (this.num === 1 && type === -1) return
this.num += Number(type)
this.$emit('counterService', this.num)
}
},
}
</script>
<style scoped>
.counter-dis, .counter-add{padding: 3px 8px;border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;}
.counter-dis{border-left: 1px solid #ddd;}
.counter-add{border-right: 1px solid #ddd;color:#06c1ae;}
.counter-num{padding: 3px 14px;border: 1px solid #ddd;}
</style>
增减数量后emit给父组件即可
最终订单提交页面在真机上显示如下
10、订单详情页面
订单详情页面由订单列表页进入,主要就是商家信息和订单信息的展示
<div class="container ub-box">
<scroll-view scroll-y style="height: 100vh" scroll-top="0">
<dl class="ub-box ub-col z-width-100-percent">
<dd class="item z-width-100-percent ub-box ub-ver z-box-sizing-border z-bg-color-fff">
<p class="ub-box">
<img class="z-img-cover" :src="formdata.img" />
</p>
<div class="ub-flex-1 ub-box ub-col z-padding-h-10-px z-box-sizing-border">
<p class="z-font-size-15 z-color-333 z-margin-bottom-3-px z-font-weight-bold z-lines-1-overflow-hidden">{{formdata.mainTitle}}</p>
<p class="z-font-size-14 z-color-666 z-margin-bottom-3-px z-lines-1-overflow-hidden">{{formdata.subTitle}}</p>
<p class="z-font-size-14 z-margin-bottom-3-px" style="color:#06c1ae">¥{{formdata.price}}</p>
</div>
</dd>
<dd class="item z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-col z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-margin-bottom-8-px z-font-weight-bold">商家信息</span>
<div class="z-width-100-percent ub-box ub-between">
<span class="z-font-size-14 z-color-666">{{formdata.sellerName}}</span>
<span @click.stop="clickContact()" class="ub-box z-padding-h-10-px z-box-sizing-border" style="border-left:1px solid #eee">
<i class="iconfont icon-dianhua z-font-size-22" style="color:#06c1ae"></i>
</span>
</div>
</dd>
<dd class="item z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-col z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-margin-bottom-8-px z-font-weight-bold">套餐</span>
<div class="ub-box ub-ver ub-between z-width-100-percent z-border-bottom-1-eee z-padding-v-10-px">
<span class="z-font-size-14 z-color-333">{{formdata.package.packageName}}</span>
<span class="z-font-size-14 z-color-333">{{formdata.package.packagePrice}}</span>
</div>
<ul class="ub-box ub-col z-width-100-percent z-margin-top-8-px">
<li v-for="(val, i) in formdata.package.notes" :key="i" class="z-font-size-14 z-color-333 z-lineHeight-22 z-margin-bottom-5-px">{{val}}</li>
</ul>
</dd>
<dd style="padding:8px 50px" class="z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-col z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-margin-bottom-8-px z-font-weight-bold">订单信息</span>
<ul class="z-width-100-percent ub-box ub-col ub-ver">
<li class="z-width-100-percent ub-box ub-between ub-ver z-margin-bottom-8-px">
<span class="z-font-size-14 z-color-888 ub-flex-1 z-textAlign-left">订单号</span>
<span class="z-font-size-14 z-color-888 ub-flex-2 z-textAlign-right">{{formdata.orderNum}}</span>
</li>
<li class="z-width-100-percent ub-box ub-between ub-ver z-margin-bottom-8-px">
<span class="z-font-size-14 z-color-888 ub-flex-1 z-textAlign-left">购买手机号</span>
<span class="z-font-size-14 z-color-888 ub-flex-2 z-textAlign-right">{{formdata.orderPhone}}</span>
</li>
<li class="z-width-100-percent ub-box ub-between ub-ver z-margin-bottom-8-px">
<span class="z-font-size-14 z-color-888 ub-flex-1 z-textAlign-left">下单时间</span>
<span class="z-font-size-14 z-color-888 ub-flex-2 z-textAlign-right">{{formdata.orderCreateTime}}</span>
</li>
<li class="z-width-100-percent ub-box ub-between ub-ver z-margin-bottom-8-px">
<span class="z-font-size-14 z-color-888 ub-flex-1 z-textAlign-left">数量</span>
<span class="z-font-size-14 z-color-888 ub-flex-2 z-textAlign-right">{{formdata.orderCount}}</span>
</li>
<li class="z-width-100-percent ub-box ub-between ub-ver">
<span class="z-font-size-14 z-color-888 ub-flex-1 z-textAlign-left">总价</span>
<span class="z-font-size-14 z-color-888 ub-flex-2 z-textAlign-right">{{formdata.orderTotalPrice}}</span>
</li>
</ul>
</dd>
<dd class="item z-margin-top-8-px z-width-100-percent ub-box ub-ver ub-col z-box-sizing-border z-bg-color-fff">
<span class="z-font-size-15 z-color-333 z-margin-bottom-8-px z-font-weight-bold">提示</span>
<p class="z-font-size-14 z-color-999 z-lineHeight-22 z-margin-bottom-8-px">您可以凭购买时所填写的手机号登录美团App进行评价、退款等更多操作</p>
<span @click.stop="clickContact()" class="contactBtn z-width-100-percent ub-box ub-ver z-font-size-14 z-color-666">联系客服</span>
</dd>
</dl>
</scroll-view>
</div>
</template>
<script>
export default {
data () {
return {
formdata: {
goodId: '100',
mainTitle: '单人自助晚餐',
subTitle: '单人自助晚餐',
img: 'http://p0.meituan.net/200.0/deal/522fd16a9b25479496188b59476d1b941062402.jpg@206_0_828_828a%7C267h_267w_2e_90Q',
sellerName: '索菲特大酒店锦厨国际餐厅自助餐',
package: {
packageName: '单人自助晚餐',
packagePrice: '¥398(1位)',
notes: [
'单人自助晚餐:周一至周四18:00-21:00',
'餐厅预定入口: https://tableplus.accorplus.com?vc=KweeZeenRestaurantAccorASE1187',
'锦厨国际餐厅位于昆明索菲特大酒店49楼,能够360度鸟瞰春城美景。',
'我们为您提供400种以上的餐食自助,全场酒水畅饮,包括葡萄酒、各色软饮及果汁。',
'更有专属定制化服务:凡周年纪念日/生日当天到店就餐的客人,将免费提供蛋糕一个(此项仅针对提前至少一天进行预约并说明过情况的客人。)',
'温馨提示:儿童按身高收费,具体是0-110cm儿童免费;110cm-140cm儿童半价;140cm以上的全价。 另:如遇特殊活动时,需根据店里实际情况按实际价格补差价。',
],
},
sellerTel: '',
price: 308.00,
orderNum: '4396693980',
orderPhone: '184****3468',
orderCreateTime: '2018/05/16 11:11:49',
orderCount: '1',
orderTotalPrice: '308',
}
}
},
methods: {
clickContact() {
wx.showActionSheet({
itemList: ['客服电话:10107888'],
success(res) {
switch(res.tapIndex) {
case 0:
wx.makePhoneCall({phoneNumber: '10107888'})
break
}
}
})
},
},
onShow() {
wx.setNavigationBarTitle({title: '订单详情页'})
}
}
</script>
<style scoped>
.container{width:100%;height:100vh;background:#f5f5f5}
.item{border-bottom: 1px solid #f5f5f5;padding: 8px 10px;}
.item img{width: 80px;height: 60px;border-radius: 3px}
.contactBtn{padding: 8px 0;border: 1px solid #eee;}
</style>
最终订单详情页面在真机上显示如下
11、通用方法封装
开发完所有页面后,我们来封装一些通用函数,方便挂载到Vue原型上在任何页面使用
首先是Ajax封装,由于微信提供的request是类似Jquery的语法里带上各个回调函数,这让我们使用习惯Promise或者es7的async/await的异步编程风格后显得非常不舒服,所以我们将request封装成Promise风格,让我们可以在任何页面通过async/await来接收服务器的返回值
封装Ajax为Promise风格的函数如下
export function Ajax (opts, cb=function(){}) {
wx.showLoading({title: '请求中...', mask: true})
const {url, method='GET', data={}} = opts
return new Promise((resolve, reject) => {
wx.request({
url,
data,
method,
header: {"content-type": "application/json"},
success(res) {
resolve(res)
},
fail(err) {
reject(err)
},
complete(res) {
setTimeout(() => {
wx.hideLoading()
cb && cb(res)
}, 1000)
}
})
})
}
下面我们简单的封装一些常用的方法,如打开新窗口,返回上一级窗口和重定向
export function openWin (url) {
wx.navigateTo({url: url})
}
// 关闭当前页面,跳转到应用内的某个页面
export function redirectTo (url) {
wx.redirectTo({url: url})
}
// 返回上一级窗口
export function backBeaforWin () {
wx.navigateBack({delta: 1})
}
最后在主程序入口里挂载即可
把通用方法挂载到Vue原型上
Vue.prototype.$openWin = openWin
Vue.prototype.$redirectTo = redirectTo
Vue.prototype.$backBeaforWin = backBeaforWin
Vue.prototype.$ajax = Ajax
12、Vuex的设计
我们在store里设置三个状态,分别为当前城市、用户是否登录标志和用户信息集合,并在mutations里提供更新维护这三个状态的方法接口即可
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
curCity: '昆明',
isLogin: false,
userInfo: {
avatarUrl: '',
city: '',
gender: '',
nickName: '',
province: ''
},
},
mutations: {
updateCity: (state, change) => {
state.curCity = change
},
updateIsLogin: (state, change) => {
state.isLogin = change
},
cleanUserInfo: (state) => {
state.userInfo = {
avatarUrl: '',
city: '',
gender: '',
nickName: '',
province: ''
}
},
updateUser: (state, change) => {
change.gender = change.gender === 1 ? '男' : '女'
state.userInfo = change
}
}
})
export default store
最后将store挂载到Vue的原型上即可
把store挂载到Vue原型上
Vue.prototype.$store = store
源码截图:
说明
如果本项目对您有帮助,欢迎 “点赞,关注” 支持一下 谢谢~
源码获取关注公众号「码农园区」,回复 【uniapp源码】