仿美团外卖小程序
开发工具
微信web开发者工具: 官网就可以下载,相信大家早就安装好了吧。 小程序 API: 官网提供的文档,不懂得地方多看两遍基本上就可以解决了。 Easy Mock: 一个能够提供虚拟数据接口的网站,在前端独自开发的情况下,实在是再好不过的工具了。 功能
已经实现的功能:
主界面 订单界面 用户界面 点菜界面 定位界面 未实现的功能:
数都数不清,毕竟大企业的产品,不是说模仿就模仿的,所以只实现了一些主要的功能,和一些能力之内的功能… 项目启动
创建界面
1."pages":[
2."pages/home/home",
3."pages/menu/menu",
4."pages/location/location",
5."pages/my/my",
6."pages/order/order"
7.],
只要编辑app.js中的pages属性,就会在项目目录下的pages文件夹里自动生成一个文件夹,里面包扩了.wxml 、 .wxss 、 .json 、 .js这样四个文件。wxml就是界面结构文件, .wxss就是样式文件, .js是用来存放js代码并实现界面逻辑的地方,至于 .json就是用来配置页面属性的地方,如:修改标题栏的颜色,和文字。
配置标题栏的样式
1."window":{
2."navigationBarTitleText": "美团外卖+",
3."navigationBarTextStyle": "white",
4."navigationBarBackgroundColor": "#FFC640"
5.},
同样是在app.json中配置,其他页面的标题栏都以此为例。
添加底栏
1."tabBar": {
2."color": "#272636",
3."selectedColor": "#FFD161",
4."backgroundColor": "#fff",
5."borderStyle": "#a8a8a8",
6."list": [
7.{
8."pagePath": "pages/home/home",
9."iconPath": "pages/images/home.png",
10."selectedIconPath": "pages/images/home-selected.png",
11."color":"white",
12."text": "首页"
13.},
14.{
15."pagePath": "pages/order/order",
16."iconPath": "pages/images/order.png",
17."selectedIconPath": "pages/images/order-selected.png",
18."text": "订单"
19.},
20.{
21."pagePath": "pages/my/my",
22."iconPath": "pages/images/my.png",
23."selectedIconPath": "pages/images/my-selected.png",
24."text": "我的"
25.}
26.]
27.}
在app.json中编写以上代码,这是小程序自带的功能,只需要照搬照抄就可以了,极其方便,效果如下:
image 数据请求
1./**
2.* 生命周期函数--监听页面显示
3.*/
4.onShow: function () {
5.var that = this;
6.wx.request({
7.url: "https://www.easy-mock.com/mock/596257bc9adc231f357c4664/restaurant/info",//easy-mock生成的虚拟数据接口链接
8.method: "GET",
9.success: function (res) {//成功得到数据,对数据进行处理
10.that.setData({//将数据发送到data中
11.restaurant: res.data.data.restaurant,
12.location: wx.getStorageSync('location')
13.})
14.}
15.});
16.},
data是每个页面.js文件中都存在的一个键,用来储存本页面需要用到的数据。具体使用,可在wxml文件中用{{‘data中的键名’}}的形式调用数据。 虚拟数据大致如下:
1.{
2."success": true,
3."data": {
4."restaurant": [{
5."name": "御膳房",
6."src": "http://i2.kiimg.com/601998/a955867016875a41.jpg",
7."star": 4.5,
8."sales": 641,
9."initial_price": 0,
10."distribution_price": 0,
11."distance": "156m",
12."time": 33
13.}, {
14."name": "韩式炸鸡啤酒屋",
15."star": 4.5,
16."sales": 731,
17."src": "http://i4.piimg.com/601998/9ce47f2f19d7717d.jpg",
18."initial_price": 15,
19."distribution_price": 0,
20."distance": "1.3km",
21."time": 52
22.},{
23.//略去
24.},{
25.//略去
26.},{
27.//...
28.}]
29.}
30.}
主界面 效果图:
image swiper控件应用
首先是两页标签的滑动切换,这里使用的是swiper,它是一款小程序自带的滑块组件,使用掌握起来非常简单,具体代码如下:
1.<swiper class="categoryList" indicator-dots="true"
2.indicator-color="rgba(228,228,228,1)"
3.indicator-active-color="#FECA49">
4.<block wx:for="{{categoryList}}" wx:key="">
5.<swiper-item>
6.<block wx:for="{{item}}" wx:key="">
7.<view class="category-info">
8.<image src="{{item.src}}"
9.class="category-image"></image>
10.<view class="category-text">{{item.name}}</view>
11.</view>
12.</block>
13.</swiper-item>
14.</block>
15.</swiper>
swiper标签就是滑块组件的主体,表示可以滑动的区域,其中indicator-dots属性是设置设置点是否显示。接下来swiper-item标签在swiper之中表示的是每一个用来作为滑动的页面。这里用包裹着swiper-item表示的是使用categoryList对象数组中数据来循环渲染swiper-item,swiper-item的数量取决于categoryList中有多少组数据。之后在swiper-item中的block标签表示的是在一个页面中用categoryList.item中的数据循环渲染多个类似的标签,这些标签就是效果图中的类别项,总共两页,每页八个。这就是swiper和循环渲染的一些基本用法。
弹出层的实现
1.<view class="mask"
2.hidden="{{mask2Hidden}}" bindtap="mask2Cancel">
3.<template is="sort_list" data="{{selected,sortSelected}}"/>
4.<scroll-view class="filterList" scroll-y="true" >
5.<view class="filterList-characteristic-title">商家特色</view>
6.<view class="filterList-characteristic-items">
7.<block wx:for="{{characteristicList}}" wx:key="">
8.<view class="filterList-characteristic-item {{characteristicSelected[index]==true?'characteristic-selected':''}}"
9.catchtap="characteristicSelected" data-index="{{index}}">{{item.text}}</view>
10.</block>
11.</view>
12.<view class="filterList-discount-title">优惠活动(单选)</view>
13.<view class="filterList-discount-items">
14.<block wx:for="{{discountList}}" wx:key="">
15.<view class="filterList-discount-item {{discountSelected==index?'discount-selected':''}}"
16.catchtap="discountSelected" data-index="{{index}}">
17.<text class="filterList-discount-item-icon"
18.style="background:{{item.iconColor}}">{{item.icon}}</text>
19.{{item.text}}</view>
20.</block>
21.</view>
22.</scroll-view>
23.<view class="filterList-footer">
24.<view class="filterList-footer-delect"
25.catchtap="clearSelectedNumb">清除筛选</view>
26.<view class="filterList-footer-finish" bindtap="finish">完成
27.<view class="filterList-footer-finish-number" hidden="{{selectedNumb==0}}">{{selectedNumb}}
28.</view>
29.</view>
30.</view>
31.</view>
最外层的mask类的view就是一个遮罩层,用来覆盖之前的界面形成遮罩的效果,并在上面显示新的界面也就是弹出层。以上的代码就是效果图中点击筛选按钮所呈现出来的内容了。其中bindtap属性就是点击事件的绑定了,具体的点击事件需要在.js文件中设置。值得一提的是,bindtap事件是会把当前标签受到的点击冒泡给它的父容器,这就相当与同时点击了他的父容器,如果想阻止冒泡的话就需要使用catchtap。
定位界面
先上效果图:
image
页面结构:
1.<view class="header">
2.<view class="search-input">
3.<input placeholder="请输入收货地址"
4.bindinput="input"></input>
5.</view>
6.<view class="search-btn">搜索</view>
7.</view>
8.<view class="result-container" hidden="{{hidden}}">
9.<scroll-view scroll-y="true"class="search-result-list" hidden="{{hidden}}">
10.<block wx:for="{{locationList}}" wx:key="">
11.<view class="search-result" bindtap="onTap" data-key="{{item.address}}">{{item.name}}
12.<view class="search-result-desc">{{item.address}}</view>
13.</view>
14.</block>
15.</scroll-view>
16.</view>
17.<view class="getLocation"
18.bindtap="getLocation">点击定位当前位置</view>
19.<view class="addLocation">新增收货地址
20.<view class="addLocation-icon">+</view>
21.</view>
22.<view class="myLocation">我的收货地址</view>
23.<view class="LocatonInfo"></view>
24.<view class="userTel"></view>
这个界面主要涉及到的就是弹出层和百度地图API的调用,调用方法可以查看百度地图API,具体点击事件代码如下:
1.getLocation: function () { 2.wx.getLocation({ 3.type: 'gcj02', 4.success: function (res) { 5.var latitude = res.latitude 6.var longitude = res.longitude 7.wx.request({ 8.url: 'http://api.map.baidu.com/geocoder/v2/?ak=btsVVWf0TM1zUBEbzFz6QqWF&coordtype=gcj02ll&location=' + latitude + ',' + longitude + '&output=json&pois=0', 9.method: "get", 10.success: function (res) { 11.console.log(res.data.result.formatted_address) 12.wx.setStorageSync('location', 13.res.data.result.formatted_address.substr(res.data.result.formatted_address.indexOf('市') + 1, 10)) 14.} 15.}) 16.} 17.}) 18.wx.switchTab({ 19.url: '/pages/home/home' 20.}) 21.}, 22.input: function (e){ 23.if(e.detail.value){ 24.this.setData({ 25.hidden: false 26.})
27.this.search(e.detail.value); 28.}else{ 29.this.setData({ 30.hidden: true 31.}) 32.} 33.}, 34.search: function (text){ 35.var that = this; 36.wx.request({ 37.url: 'http://api.map.baidu.com/place/v2/search?query=' + text +'&page_size=20&page_num=0&scope=2®ion=南昌&output=json&ak=btsVVWf0TM1zUBEbzFz6QqWF', 38.success: function(res){ 39.console.log(res); 40.that.setData({ 41.locationList:res.data.results 42.}) 43.} 44.}) 45.},
点菜界面 效果图如下:
image
页面结构如下:
1.<import src = "../common/orderPage.wxml"/>
2.<import src = "../common/commentPage.wxml"/>
3.<view class="container" disable-scroll="true">
4.<view class="header">
5.<block wx:for="{{swiperTitle}}" wx:key="">
5.<view class="title {{index==currentPage?'selected':''}}" data-index="{{index}}
6.bindtap="turnPage">{{item.text}}</view>
7.</block>
8.</view>
9.<swiper class="swiper" current="{{currentPage}}
10.bindchange="turnTitle">
11.<swiper-item id="orderPage">
12.<template is="orderPage" data="{{menu,selected,howMuch,cost,pullBar}}"/>
13.</swiper-item>
14.<swiper-item id="commentPage">
15.<template is="commentPage" data="{{categoryList}}"/>
16.</swiper-item>
17.<swiper-item id="restaurantPage"></swiper-item>
18.</swiper>
19.</view>
菜单页面如下:<template name=”orderPage”>
2.<scroll-view class=”orderPage-sideBar”
3.bindscrolltolower=”lower”
- scroll-y=”true”>
- <block wx:for=”{{menu}}” wx:key=””>
- <view class=”menuList”>
- <view class=”menu {{index==selected?’selected’:”}}”
- data-index=”{{index}}”
- catchtap=”turnMenu”>{{item.typeName}}</view>
- </view>
- </block>
- </scroll-view>
- <scroll-view class=”foodList” scroll-y=”true”>
- <view class=”title”>{{menu[selected].typeName}}</view>
- <block wx:for=”{{menu[selected].menuContent}}” wx:key=””>
- <view class=”food”>
- <image class=”img” src=”{{item.src}}”></image>
- <view class=”food-info”>
- <view class=”name”>{{item.name}}</view>
- <view class=”sales”>月售 {{item.sales}} 赞 {{item.rating}}
- </view>
- <view class=”price”>¥ {{item.price}}</view>
- </view>
- <view class=”food-numb”>
- <view class=”remove”
- bindtap=”removeFromTrolley”
- hidden=”{{item.numb==0}}”
- data-index=”{{index}}”>-</view>
- <text class=”text”
- hidden=”{{item.numb==0}}”>{{item.numb}}</text>
- <view class=”add”
- bindtap=”addToTrolley” data-index=”{{index}}”>+</view>
- </view>
- </view>
- </block>
- </scroll-view>
- <view class=”footer {{cost!=0?’active’:”}}”>
- <view class=”howMuch”>
- <view class=”img” style=”background:{{cost!=0?’#FFD161′:’#E7E7E7′}};”>
- <image src=”/pages/images/trolley.png” style=”width:60rpx;height:60rpx;”></image>
- </view>
- <view class=”cost” hidden=”{{cost==0}}”>¥{{cost}}</view>
- <view class=”free”>免配送费</view>
- </view>
- <view class=”pay”>{{cost!=0?’去结算’:’15元起送’}}</view>
- </view>
- </template>
tab切换
这个界面最主要的功能就是tab切换,和点菜功能。其中tab切换其实用的还是swiper,因为swiper有一个current属性表示的是swiper当下显示的页面的序号,只需要将tab中被激活的项与swiper的页面互相绑定就可以了,具体代码如下:
1.turnPage: function (e) {
2.this.setData({
3.currentPage: e.currentTarget.dataset.index
4.})
5.},
6.turnTitle: function (e) {
7.if(e.detail.source=="touch"){//判断是否是滑动引起的界面切换
8.this.setData({
9.currentPage: e.detail.current
10.})
11.}
12.},
当点击title中的项时获取当前序号,再将它赋值给current,当手指滑动swiper时触发bindchange事件,获取当前页面序号,使相应序号的title处于被选中的状态。有一个值得注意的地方是当点击title中的项时也会触发swiper的bindchange事件,但是我们只想让它在滑动swiper时触发,否则就会出现setData过于频繁的警告,所以我们需要在turnTitle中加一段判断语句,判断页面滑动的原因是否为滑动,如果不是则不执行下面的语句。
下载地址:仿美团外卖小程序